X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fprops%2Fprops.cxx;h=acbcc92dfb7ba8a1bdda4aba870c111af24c36db;hb=fc4009aa505702241a1b79c710c4358e686245b9;hp=c6f5145b1e20bd1cbe5d1afff0d496d1100b191b;hpb=3c08eae85b77e329d34245fa6f9200c08e52e3bb;p=simgear.git diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index c6f5145b..acbcc92d 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -6,39 +6,55 @@ // // $Id$ +#ifdef HAVE_CONFIG_H +# include +#endif + #include "props.hxx" #include + +#include +#include +#include #include #include -#if PROPS_STANDALONE +#include +#include +#include +#include +#include +#include -#include -using std::cerr; -using std::endl; -using std::find; -using std::sort; -using std::vector; +#include +#if PROPS_STANDALONE +#include #else #include #include -SG_USING_STD(sort); -SG_USING_STD(find); -SG_USING_STD(vector); - -#ifdef _MSC_VER -// MSVC is buggy, and needs something strange here -SG_USING_STD(vector); -SG_USING_STD(vector); -SG_USING_STD(vector); +#if ( _MSC_VER == 1200 ) +// MSVC 6 is buggy, and needs something strange here +using std::vector; +using std::vector; +using std::vector; +#endif #endif +#if PROPS_STANDALONE +using std::cerr; #endif +using std::endl; +using std::find; +using std::sort; +using std::string; +using std::vector; +using std::stringstream; +using namespace simgear; //////////////////////////////////////////////////////////////////////// @@ -64,69 +80,42 @@ public: #define TEST_READ(dflt) if (!getAttribute(READ)) return dflt #define TEST_WRITE if (!getAttribute(WRITE)) return false - - -//////////////////////////////////////////////////////////////////////// -// 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 char * const SGRawValue::DefaultValue = ""; - - //////////////////////////////////////////////////////////////////////// // Local path normalization code. //////////////////////////////////////////////////////////////////////// -/** - * A component in a path. - */ -struct PathComponent -{ - string name; - int index; -}; - /** * Parse the name for a path component. * * Name: [_a-zA-Z][-._a-zA-Z0-9]* */ -static inline const string -parse_name (const string &path, int &i) + +template +inline Range +parse_name (const Range &path) { - string name = ""; - int max = path.size(); + typename Range::iterator i = path.begin(); + typename Range::iterator max = path.end(); - if (path[i] == '.') { + if (*i == '.') { i++; - if (i < max && path[i] == '.') { + if (i != path.end() && *i == '.') { i++; - name = ".."; - } else { - name = "."; } - if (i < max && path[i] != '/') - throw string("Illegal character after " + name); - } - - else if (isalpha(path[i]) || path[i] == '_') { - name += path[i]; + if (i != max && *i != '/') + throw string("illegal character after . or .."); + } else if (isalpha(*i) || *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] == '/') { + while (i != max) { + if (isalpha(*i) || isdigit(*i) || *i == '_' || + *i == '-' || *i == '.') { + // name += path[i]; + } else if (*i == '[' || *i == '/') { break; } else { throw string("name may contain only ._- and alphanumeric characters"); @@ -136,90 +125,24 @@ parse_name (const string &path, int &i) } else { - if (name.size() == 0) + if (path.begin() == i) throw string("name must begin with alpha or '_'"); } - - return name; -} - - -/** - * Parse the optional integer index for a path component. - * - * Index: "[" [0-9]+ "]" - */ -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 ']')"); -} - - -/** - * Parse a single path component. - * - * Component: Name Index? - */ -static inline PathComponent -parse_component (const string &path, int &i) -{ - PathComponent component; - component.name = parse_name(path, i); - if (component.name[0] != '.') - component.index = parse_index(path, i); - else - component.index = -1; - return component; + return Range(path.begin(), i); } - -/** - * Parse a path into its components. - */ -static void -parse_path (const string &path, vector &components) +// Validate the name of a single node +inline bool validateName(const string& name) { - 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++; - } + using namespace boost; + if (name.empty()) + return false; + if (!isalpha(name[0]) && name[0] != '_') + return false; + return all(make_iterator_range(name.begin(), name.end()), + is_alnum() || is_any_of("_-.")); } - //////////////////////////////////////////////////////////////////////// // Other static utility functions. @@ -229,11 +152,13 @@ parse_path (const string &path, vector &components) static char * copy_string (const char * s) { - // FIXME: potential buffer overflow. - // For some reason, strnlen and - // strncpy cause all kinds of crashes. - char * copy = new char[strlen(s) + 1]; - strcpy(copy, s); + unsigned long int slen = strlen(s); + char * copy = new char[slen + 1]; + + // the source string length is known so no need to check for '\0' + // when copying every single character + memcpy(copy, s, slen); + *(copy + slen) = '\0'; return copy; } @@ -246,67 +171,174 @@ compare_strings (const char * s1, const char * s2) /** * Locate a child node by name and index. */ +template static int -find_child (const char * name, int index, vector nodes) +find_child (Itr begin, Itr end, int index, const PropertyList& nodes) { int nNodes = nodes.size(); + boost::iterator_range name(begin, end); for (int i = 0; i < nNodes; i++) { SGPropertyNode * node = nodes[i]; - if (compare_strings(node->getName(), name) && node->getIndex() == index) + + // searching for a mathing index is a lot less time consuming than + // comparing two strings so do that first. + if (node->getIndex() == index && boost::equals(node->getName(), name)) return i; } return -1; } - /** - * Locate another node, given a relative path. + * Locate the child node with the highest index of the same name */ -static SGPropertyNode * -find_node (SGPropertyNode * current, - const vector &components, - int position, - bool create) +static int +find_last_child (const char * name, const PropertyList& nodes) { - // Run off the end of the list - if (current == 0) { - return 0; - } + int nNodes = nodes.size(); + int index = 0; - // Success! This is the one we want. - else if (position >= (int)components.size()) { - return current; + for (int i = 0; i < nNodes; i++) { + SGPropertyNode * node = nodes[i]; + if (compare_strings(node->getName(), name)) + { + int idx = node->getIndex(); + if (idx > index) index = idx; + } } + return index; +} - // Empty component means root. - else if (components[position].name == "") { - return find_node(current->getRootNode(), components, position + 1, create); +template +inline SGPropertyNode* +SGPropertyNode::getExistingChild (Itr begin, Itr end, int index, bool create) +{ + int pos = find_child(begin, end, index, _children); + if (pos >= 0) { + return _children[pos]; + } else if (create) { + SGPropertyNode_ptr node; + pos = find_child(begin, end, index, _removedChildren); + if (pos >= 0) { + PropertyList::iterator it = _removedChildren.begin(); + it += pos; + node = _removedChildren[pos]; + _removedChildren.erase(it); + node->setAttribute(REMOVED, false); + _children.push_back(node); + fireChildAdded(node); + return node; + } } + return 0; +} + +template +SGPropertyNode * +SGPropertyNode::getChildImpl (Itr begin, Itr end, int index, bool create) +{ + SGPropertyNode* node = getExistingChild(begin, end, index, create); - // . means current directory - else if (components[position].name == ".") { - return find_node(current, components, position + 1, create); + if (node) { + return node; + } else if (create) { + node = new SGPropertyNode(begin, end, index, this); + _children.push_back(node); + fireChildAdded(node); + return node; + } else { + return 0; + } +} + +template +SGPropertyNode* +find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create, + int last_index) +{ + typedef typename SplitItr::value_type Range; + // Run off the end of the list + if (current == 0) { + return 0; } - // .. means parent directory - else if (components[position].name == "..") { + // Success! This is the one we want. + if (itr.eof()) + return current; + Range token = *itr; + // Empty name at this point is empty, not root. + if (token.empty()) + return find_node_aux(current, ++itr, create, last_index); + Range name = parse_name(token); + if (equals(name, ".")) + return find_node_aux(current, ++itr, create, last_index); + if (equals(name, "..")) { SGPropertyNode * parent = current->getParent(); if (parent == 0) - throw string("Attempt to move past root with '..'"); - else - 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); + throw string("attempt to move past root with '..'"); + return find_node_aux(parent, ++itr, create, last_index); + } + int index = -1; + if (last_index >= 0) { + // If we are at the last token and last_index is valid, use + // last_index as the index value + bool lastTok = true; + while (!(++itr).eof()) { + if (!itr->empty()) { + lastTok = false; + break; + } + } + if (lastTok) + index = last_index; + } else { + ++itr; + } + + if (index < 0) { + index = 0; + if (name.end() != token.end()) { + if (*name.end() == '[') { + typename Range::iterator i = name.end() + 1, end = token.end(); + for (;i != end; ++i) { + if (isdigit(*i)) { + index = (index * 10) + (*i - '0'); + } else { + break; + } + } + if (i == token.end() || *i != ']') + throw string("unterminated index (looking for ']')"); + } else { + throw string("illegal characters in token: ") + + string(name.begin(), name.end()); + } + } } + return find_node_aux(current->getChildImpl(name.begin(), name.end(), + index, create), itr, create, + last_index); } +// Internal function for parsing property paths. last_index provides +// and index value for the last node name token, if supplied. +template +SGPropertyNode* +find_node (SGPropertyNode * current, + const Range& path, + bool create, + int last_index = -1) +{ + using namespace boost; + typedef split_iterator::type> + PathSplitIterator; + + PathSplitIterator itr + = make_split_iterator(path, first_finder("/", is_equal())); + if (*path.begin() == '/') + return find_node_aux(current->getRootNode(), itr, create, last_index); + else + return find_node_aux(current, itr, create, last_index); +} //////////////////////////////////////////////////////////////////////// @@ -317,7 +349,7 @@ inline bool SGPropertyNode::get_bool () const { if (_tied) - return _value.bool_val->getValue(); + return static_cast*>(_value.val)->getValue(); else return _local_val.bool_val; } @@ -326,7 +358,7 @@ inline int SGPropertyNode::get_int () const { if (_tied) - return _value.int_val->getValue(); + return (static_cast*>(_value.val))->getValue(); else return _local_val.int_val; } @@ -335,7 +367,7 @@ inline long SGPropertyNode::get_long () const { if (_tied) - return _value.long_val->getValue(); + return static_cast*>(_value.val)->getValue(); else return _local_val.long_val; } @@ -344,7 +376,7 @@ inline float SGPropertyNode::get_float () const { if (_tied) - return _value.float_val->getValue(); + return static_cast*>(_value.val)->getValue(); else return _local_val.float_val; } @@ -353,7 +385,7 @@ inline double SGPropertyNode::get_double () const { if (_tied) - return _value.double_val->getValue(); + return static_cast*>(_value.val)->getValue(); else return _local_val.double_val; } @@ -362,7 +394,7 @@ inline const char * SGPropertyNode::get_string () const { if (_tied) - return _value.string_val->getValue(); + return static_cast*>(_value.val)->getValue(); else return _local_val.string_val; } @@ -371,7 +403,7 @@ inline bool SGPropertyNode::set_bool (bool val) { if (_tied) { - if (_value.bool_val->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -388,7 +420,7 @@ inline bool SGPropertyNode::set_int (int val) { if (_tied) { - if (_value.int_val->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -405,7 +437,7 @@ inline bool SGPropertyNode::set_long (long val) { if (_tied) { - if (_value.long_val->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -422,7 +454,7 @@ inline bool SGPropertyNode::set_float (float val) { if (_tied) { - if (_value.float_val->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -439,7 +471,7 @@ inline bool SGPropertyNode::set_double (double val) { if (_tied) { - if (_value.double_val->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -456,7 +488,7 @@ inline bool SGPropertyNode::set_string (const char * val) { if (_tied) { - if (_value.string_val->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -471,62 +503,43 @@ SGPropertyNode::set_string (const char * val) } 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; +SGPropertyNode::clearValue () +{ + if (_type == props::ALIAS) { + put(_value.alias); + _value.alias = 0; + } else if (_type != props::NONE) { + switch (_type) { + case props::BOOL: + _local_val.bool_val = SGRawValue::DefaultValue(); + break; + case props::INT: + _local_val.int_val = SGRawValue::DefaultValue(); + break; + case props::LONG: + _local_val.long_val = SGRawValue::DefaultValue(); + break; + case props::FLOAT: + _local_val.float_val = SGRawValue::DefaultValue(); + break; + case props::DOUBLE: + _local_val.double_val = SGRawValue::DefaultValue(); + break; + case props::STRING: + case props::UNSPECIFIED: + if (!_tied) { + delete [] _local_val.string_val; + } + _local_val.string_val = 0; + break; + default: // avoid compiler warning + break; + } + delete _value.val; + _value.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 [] _local_val.string_val; - } - _local_val.string_val = 0; - break; - } - _tied = false; - _type = NONE; + _tied = false; + _type = props::NONE; } @@ -536,36 +549,49 @@ SGPropertyNode::clear_value () const char * SGPropertyNode::make_string () const { - if (!getAttribute(READ)) - return ""; - - switch (_type) { - case ALIAS: - return _value.alias->getStringValue(); - case BOOL: - if (get_bool()) - return "true"; - else - 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 ""; - } + if (!getAttribute(READ)) + return ""; + switch (_type) { + case props::ALIAS: + return _value.alias->getStringValue(); + case props::BOOL: + return get_bool() ? "true" : "false"; + case props::STRING: + case props::UNSPECIFIED: + return get_string(); + case props::NONE: + return ""; + default: + break; + } + stringstream sstr; + switch (_type) { + case props::INT: + sstr << get_int(); + break; + case props::LONG: + sstr << get_long(); + break; + case props::FLOAT: + sstr << get_float(); + break; + case props::DOUBLE: + sstr << std::setprecision(10) << get_double(); + break; + case props::EXTENDED: + { + props::Type realType = _value.val->getType(); + // Perhaps this should be done for all types? + if (realType == props::VEC3D || realType == props::VEC4D) + sstr.precision(10); + static_cast(_value.val)->printOn(sstr); + } + break; + default: + return ""; + } + _buffer = sstr.str(); + return _buffer.c_str(); } /** @@ -575,11 +601,11 @@ void SGPropertyNode::trace_write () const { #if PROPS_STANDALONE - cerr << "TRACE: Write node " << getPath () << ", value\"" + cerr << "TRACE: Write node " << getPath () << ", value \"" << make_string() << '"' << endl; #else - SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath() - << ", value\"" << make_string() << '"'); + SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << getPath() + << ", value \"" << make_string() << '"'); #endif } @@ -593,30 +619,11 @@ SGPropertyNode::trace_read () const cerr << "TRACE: Write node " << getPath () << ", value \"" << make_string() << '"' << endl; #else - SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath() + SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << getPath() << ", value \"" << make_string() << '"'); #endif } -/** - * Increment reference counter - */ -void -SGPropertyNode::incrementRef() -{ - ++_count; -} - -/** - * Decrement reference counter - */ -int -SGPropertyNode::decrementRef() -{ - return --_count; -} - - //////////////////////////////////////////////////////////////////////// // Public methods from SGPropertyNode. @@ -626,25 +633,22 @@ SGPropertyNode::decrementRef() * Last used attribute * Update as needed when enum Attribute is changed */ -const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE; +const int SGPropertyNode::LAST_USED_ATTRIBUTE = USERARCHIVE; /** * Default constructor: always creates a root node. */ SGPropertyNode::SGPropertyNode () - : _name(copy_string("")), - _display_name(0), - _index(0), + : _index(0), _parent(0), - _path(0), _path_cache(0), - _type(NONE), + _type(props::NONE), _tied(false), _attr(READ|WRITE), - _count(0), _listeners(0) { _local_val.string_val = 0; + _value.val = 0; } @@ -652,80 +656,50 @@ SGPropertyNode::SGPropertyNode () * Copy constructor. */ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) - : _display_name(0), - _index(node._index), + : _index(node._index), + _name(node._name), _parent(0), // don't copy the parent - _path(0), _path_cache(0), _type(node._type), _tied(node._tied), _attr(node._attr), - _count(0), _listeners(0) // CHECK!! { - _name = copy_string(node._name); _local_val.string_val = 0; - switch (_type) { - case NONE: - break; - case ALIAS: + _value.val = 0; + if (_type == props::NONE) + return; + if (_type == props::ALIAS) { _value.alias = node._value.alias; + get(_value.alias); _tied = false; + return; + } + if (_tied || _type == props::EXTENDED) { + _value.val = node._value.val->clone(); + return; + } + switch (_type) { + case props::BOOL: + set_bool(node.get_bool()); break; - case BOOL: - if (_tied) { - _tied = true; - _value.bool_val = node._value.bool_val->clone(); - } else { - _tied = false; - set_bool(node.get_bool()); - } + case props::INT: + set_int(node.get_int()); break; - case INT: - if (_tied) { - _tied = true; - _value.int_val = node._value.int_val->clone(); - } else { - _tied = false; - set_int(node.get_int()); - } + case props::LONG: + set_long(node.get_long()); break; - case LONG: - if (_tied) { - _tied = true; - _value.long_val = node._value.long_val->clone(); - } else { - _tied = false; - set_long(node.get_long()); - } + case props::FLOAT: + set_float(node.get_float()); break; - case FLOAT: - if (_tied) { - _tied = true; - _value.float_val = node._value.float_val->clone(); - } else { - _tied = false; - set_float(node.get_float()); - } + case props::DOUBLE: + set_double(node.get_double()); break; - case DOUBLE: - if (_tied) { - _tied = true; - _value.double_val = node._value.double_val->clone(); - } else { - _tied = false; - set_double(node.get_double()); - } + case props::STRING: + case props::UNSPECIFIED: + set_string(node.get_string()); 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()); - } + default: break; } } @@ -734,36 +708,62 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) /** * Convenience constructor. */ -SGPropertyNode::SGPropertyNode (const char * name, +template +SGPropertyNode::SGPropertyNode (Itr begin, Itr end, int index, SGPropertyNode * parent) - : _display_name(0), - _index(index), + : _index(index), + _name(begin, end), _parent(parent), - _path(0), _path_cache(0), - _type(NONE), + _type(props::NONE), _tied(false), _attr(READ|WRITE), - _count(0), _listeners(0) { - _name = copy_string(name); _local_val.string_val = 0; + _value.val = 0; + if (!validateName(_name)) + throw string("plain name expected instead of '") + _name + '\''; } +SGPropertyNode::SGPropertyNode (const string& name, + int index, + SGPropertyNode * parent) + : _index(index), + _name(name), + _parent(parent), + _path_cache(0), + _type(props::NONE), + _tied(false), + _attr(READ|WRITE), + _listeners(0) +{ + _local_val.string_val = 0; + _value.val = 0; + if (!validateName(name)) + throw string("plain name expected instead of '") + _name + '\''; +} /** * Destructor. */ SGPropertyNode::~SGPropertyNode () { - delete [] _name; - delete [] _display_name; - delete [] _path; + // zero out all parent pointers, else they might be dangling + for (unsigned i = 0; i < _children.size(); ++i) + _children[i]->_parent = 0; + for (unsigned i = 0; i < _removedChildren.size(); ++i) + _removedChildren[i]->_parent = 0; delete _path_cache; - clear_value(); - delete _listeners; + clearValue(); + + if (_listeners) { + vector::iterator it; + for (it = _listeners->begin(); it != _listeners->end(); ++it) + (*it)->unregister_property(this); + delete _listeners; + } } @@ -773,11 +773,12 @@ SGPropertyNode::~SGPropertyNode () bool SGPropertyNode::alias (SGPropertyNode * target) { - if (target == 0 || _type == ALIAS || _tied) + if (target == 0 || _type == props::ALIAS || _tied) return false; - clear_value(); + clearValue(); + get(target); _value.alias = target; - _type = ALIAS; + _type = props::ALIAS; return true; } @@ -798,10 +799,9 @@ SGPropertyNode::alias (const char * path) bool SGPropertyNode::unalias () { - if (_type != ALIAS) + if (_type != props::ALIAS) return false; - _type = NONE; - _value.alias = 0; + clearValue(); return true; } @@ -812,14 +812,29 @@ SGPropertyNode::unalias () SGPropertyNode * SGPropertyNode::getAliasTarget () { - return (_type == ALIAS ? _value.alias : 0); + return (_type == props::ALIAS ? _value.alias : 0); } const SGPropertyNode * SGPropertyNode::getAliasTarget () const { - return (_type == ALIAS ? _value.alias : 0); + return (_type == props::ALIAS ? _value.alias : 0); +} + +/** + * create a non-const child by name after the last node with the same name. + */ +SGPropertyNode * +SGPropertyNode::addChild (const char * name) +{ + int pos = find_last_child(name, _children)+1; + + SGPropertyNode_ptr node; + node = new SGPropertyNode(name, name + strlen(name), pos, this); + _children.push_back(node); + fireChildAdded(node); + return node; } @@ -852,40 +867,37 @@ SGPropertyNode::getChild (int position) const /** * Get a non-const child by name and index, creating if necessary. */ + SGPropertyNode * SGPropertyNode::getChild (const char * name, int index, bool create) { - int pos = find_child(name, index, _children); - if (pos >= 0) { - return _children[pos]; - } else if (create) { - SGPropertyNode_ptr node; - pos = find_child(name, index, _removedChildren); - if (pos >= 0) { - vector::iterator it = _removedChildren.begin(); - it += pos; - node = _removedChildren[pos]; - _removedChildren.erase(it); - node->setAttribute(REMOVED, false); - } else { + return getChildImpl(name, name + strlen(name), index, create); +} + +SGPropertyNode * +SGPropertyNode::getChild (const string& name, int index, bool create) +{ + SGPropertyNode* node = getExistingChild(name.begin(), name.end(), index, + create); + if (node) { + return node; + } else if (create) { node = new SGPropertyNode(name, index, this); + _children.push_back(node); + fireChildAdded(node); + return node; + } else { + return 0; } - _children.push_back(node); - fireChildAdded(node); - return node; - } else { - return 0; - } } - /** * Get a const child by name and index. */ const SGPropertyNode * SGPropertyNode::getChild (const char * name, int index) const { - int pos = find_child(name, index, _children); + int pos = find_child(name, name + strlen(name), index, _children); if (pos >= 0) return _children[pos]; else @@ -896,10 +908,10 @@ SGPropertyNode::getChild (const char * name, int index) const /** * Get all children with the same name (but different indices). */ -vector +PropertyList SGPropertyNode::getChildren (const char * name) const { - vector children; + PropertyList children; int max = _children.size(); for (int i = 0; i < max; i++) @@ -911,68 +923,135 @@ SGPropertyNode::getChildren (const char * name) const } +/** + * Remove this node and all children from nodes that link to them + * in their path cache. + */ +void +SGPropertyNode::remove_from_path_caches () +{ + for (unsigned int i = 0; i < _children.size(); ++i) + _children[i]->remove_from_path_caches(); + + for (unsigned int i = 0; i < _linkedNodes.size(); i++) + _linkedNodes[i]->erase(this); + _linkedNodes.clear(); +} + + +/** + * Remove child by position. + */ +SGPropertyNode_ptr +SGPropertyNode::removeChild (int pos, bool keep) +{ + SGPropertyNode_ptr node; + if (pos < 0 || pos >= (int)_children.size()) + return node; + + PropertyList::iterator it = _children.begin(); + it += pos; + node = _children[pos]; + _children.erase(it); + if (keep) { + _removedChildren.push_back(node); + } + + node->remove_from_path_caches(); + node->setAttribute(REMOVED, true); + node->clearValue(); + fireChildRemoved(node); + return node; +} + + /** * Remove a child node */ -SGPropertyNode_ptr +SGPropertyNode_ptr SGPropertyNode::removeChild (const char * name, int index, bool keep) { SGPropertyNode_ptr ret; - int pos = find_child(name, index, _children); - if (pos >= 0) { - vector::iterator it = _children.begin(); - it += pos; - SGPropertyNode_ptr node = _children[pos]; - _children.erase(it); - if (keep) { - _removedChildren.push_back(node); + int pos = find_child(name, name + strlen(name), index, _children); + if (pos >= 0) + ret = removeChild(pos, keep); + return ret; +} + + +/** + * Remove all children with the specified name. + */ +PropertyList +SGPropertyNode::removeChildren (const char * name, bool keep) +{ + PropertyList children; + + for (int pos = _children.size() - 1; pos >= 0; pos--) + if (compare_strings(_children[pos]->getName(), name)) + children.push_back(removeChild(pos, keep)); + + sort(children.begin(), children.end(), CompareIndices()); + return children; +} + + +/** + * Remove a linked node. + */ +bool +SGPropertyNode::remove_linked_node (hash_table * node) +{ + for (unsigned int i = 0; i < _linkedNodes.size(); i++) { + if (_linkedNodes[i] == node) { + vector::iterator it = _linkedNodes.begin(); + it += i; + _linkedNodes.erase(it); + return true; } - node->setAttribute(REMOVED, true); - ret = node; - fireChildRemoved(node); } - return ret; + return false; } -const char * +string SGPropertyNode::getDisplayName (bool simplify) const { - string display = _name; + string display_name = _name; if (_index != 0 || !simplify) { - char buffer[64]; - sprintf(buffer, "[%d]", _index); - display += buffer; + stringstream sstr; + sstr << '[' << _index << ']'; + display_name += sstr.str(); } - _display_name = copy_string(display.c_str()); - return _display_name; + return display_name; } -const char * +string SGPropertyNode::getPath (bool simplify) const { - // Calculate the complete path only once. - if (_path == 0) { - string path; - if (_parent == 0) { - path = ""; - } else { - path = _parent->getPath(simplify); - path += '/'; - path += getDisplayName(simplify); - } - _path = copy_string(path.c_str()); + typedef std::vector PList; + PList pathList; + for (const SGPropertyNode* node = this; node->_parent; node = node->_parent) + pathList.push_back(node); + string result; + for (PList::reverse_iterator itr = pathList.rbegin(), + rend = pathList.rend(); + itr != rend; + ++itr) { + result += '/'; + result += (*itr)->getDisplayName(simplify); } - - return _path; + return result; } -SGPropertyNode::Type +props::Type SGPropertyNode::getType () const { - if (_type == ALIAS) + if (_type == props::ALIAS) return _value.alias->getType(); + else if (_type == props::EXTENDED) + return _value.val->getType(); else return _type; } @@ -982,32 +1061,32 @@ bool SGPropertyNode::getBoolValue () const { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == BOOL) + if (_attr == (READ|WRITE) && _type == props::BOOL) return get_bool(); if (getAttribute(TRACE_READ)) trace_read(); if (!getAttribute(READ)) - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); switch (_type) { - case ALIAS: + case props::ALIAS: return _value.alias->getBoolValue(); - case BOOL: + case props::BOOL: return get_bool(); - case INT: + case props::INT: return get_int() == 0 ? false : true; - case LONG: + case props::LONG: return get_long() == 0L ? false : true; - case FLOAT: + case props::FLOAT: return get_float() == 0.0 ? false : true; - case DOUBLE: + case props::DOUBLE: return get_double() == 0.0L ? false : true; - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L); - case NONE: + case props::NONE: default: - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); } } @@ -1015,32 +1094,32 @@ int SGPropertyNode::getIntValue () const { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == INT) + if (_attr == (READ|WRITE) && _type == props::INT) return get_int(); if (getAttribute(TRACE_READ)) trace_read(); if (!getAttribute(READ)) - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); switch (_type) { - case ALIAS: + case props::ALIAS: return _value.alias->getIntValue(); - case BOOL: + case props::BOOL: return int(get_bool()); - case INT: + case props::INT: return get_int(); - case LONG: + case props::LONG: return int(get_long()); - case FLOAT: + case props::FLOAT: return int(get_float()); - case DOUBLE: + case props::DOUBLE: return int(get_double()); - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: return atoi(get_string()); - case NONE: + case props::NONE: default: - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); } } @@ -1048,32 +1127,32 @@ long SGPropertyNode::getLongValue () const { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == LONG) + if (_attr == (READ|WRITE) && _type == props::LONG) return get_long(); if (getAttribute(TRACE_READ)) trace_read(); if (!getAttribute(READ)) - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); switch (_type) { - case ALIAS: + case props::ALIAS: return _value.alias->getLongValue(); - case BOOL: + case props::BOOL: return long(get_bool()); - case INT: + case props::INT: return long(get_int()); - case LONG: + case props::LONG: return get_long(); - case FLOAT: + case props::FLOAT: return long(get_float()); - case DOUBLE: + case props::DOUBLE: return long(get_double()); - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: return strtol(get_string(), 0, 0); - case NONE: + case props::NONE: default: - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); } } @@ -1081,32 +1160,32 @@ float SGPropertyNode::getFloatValue () const { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == FLOAT) + if (_attr == (READ|WRITE) && _type == props::FLOAT) return get_float(); if (getAttribute(TRACE_READ)) trace_read(); if (!getAttribute(READ)) - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); switch (_type) { - case ALIAS: + case props::ALIAS: return _value.alias->getFloatValue(); - case BOOL: + case props::BOOL: return float(get_bool()); - case INT: + case props::INT: return float(get_int()); - case LONG: + case props::LONG: return float(get_long()); - case FLOAT: + case props::FLOAT: return get_float(); - case DOUBLE: + case props::DOUBLE: return float(get_double()); - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: return atof(get_string()); - case NONE: + case props::NONE: default: - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); } } @@ -1114,33 +1193,33 @@ double SGPropertyNode::getDoubleValue () const { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == DOUBLE) + if (_attr == (READ|WRITE) && _type == props::DOUBLE) return get_double(); if (getAttribute(TRACE_READ)) trace_read(); if (!getAttribute(READ)) - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); switch (_type) { - case ALIAS: + case props::ALIAS: return _value.alias->getDoubleValue(); - case BOOL: + case props::BOOL: return double(get_bool()); - case INT: + case props::INT: return double(get_int()); - case LONG: + case props::LONG: return double(get_long()); - case FLOAT: + case props::FLOAT: return double(get_float()); - case DOUBLE: + case props::DOUBLE: return get_double(); - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: return strtod(get_string(), 0); - case NONE: + case props::NONE: default: - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); } } @@ -1148,13 +1227,13 @@ const char * SGPropertyNode::getStringValue () const { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == STRING) + if (_attr == (READ|WRITE) && _type == props::STRING) return get_string(); if (getAttribute(TRACE_READ)) trace_read(); if (!getAttribute(READ)) - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); return make_string(); } @@ -1162,41 +1241,41 @@ bool SGPropertyNode::setBoolValue (bool value) { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == BOOL) + if (_attr == (READ|WRITE) && _type == props::BOOL) return set_bool(value); bool result = false; TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); + if (_type == props::NONE || _type == props::UNSPECIFIED) { + clearValue(); _tied = false; - _type = BOOL; + _type = props::BOOL; } switch (_type) { - case ALIAS: + case props::ALIAS: result = _value.alias->setBoolValue(value); break; - case BOOL: + case props::BOOL: result = set_bool(value); break; - case INT: + case props::INT: result = set_int(int(value)); break; - case LONG: + case props::LONG: result = set_long(long(value)); break; - case FLOAT: + case props::FLOAT: result = set_float(float(value)); break; - case DOUBLE: + case props::DOUBLE: result = set_double(double(value)); break; - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: result = set_string(value ? "true" : "false"); break; - case NONE: + case props::NONE: default: break; } @@ -1210,44 +1289,44 @@ bool SGPropertyNode::setIntValue (int value) { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == INT) + if (_attr == (READ|WRITE) && _type == props::INT) return set_int(value); bool result = false; TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = INT; + if (_type == props::NONE || _type == props::UNSPECIFIED) { + clearValue(); + _type = props::INT; _local_val.int_val = 0; } switch (_type) { - case ALIAS: + case props::ALIAS: result = _value.alias->setIntValue(value); break; - case BOOL: + case props::BOOL: result = set_bool(value == 0 ? false : true); break; - case INT: + case props::INT: result = set_int(value); break; - case LONG: + case props::LONG: result = set_long(long(value)); break; - case FLOAT: + case props::FLOAT: result = set_float(float(value)); break; - case DOUBLE: + case props::DOUBLE: result = set_double(double(value)); break; - case STRING: - case UNSPECIFIED: { + case props::STRING: + case props::UNSPECIFIED: { char buf[128]; sprintf(buf, "%d", value); result = set_string(buf); break; } - case NONE: + case props::NONE: default: break; } @@ -1261,44 +1340,44 @@ bool SGPropertyNode::setLongValue (long value) { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == LONG) + if (_attr == (READ|WRITE) && _type == props::LONG) return set_long(value); bool result = false; TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = LONG; + if (_type == props::NONE || _type == props::UNSPECIFIED) { + clearValue(); + _type = props::LONG; _local_val.long_val = 0L; } switch (_type) { - case ALIAS: + case props::ALIAS: result = _value.alias->setLongValue(value); break; - case BOOL: + case props::BOOL: result = set_bool(value == 0L ? false : true); break; - case INT: + case props::INT: result = set_int(int(value)); break; - case LONG: + case props::LONG: result = set_long(value); break; - case FLOAT: + case props::FLOAT: result = set_float(float(value)); break; - case DOUBLE: + case props::DOUBLE: result = set_double(double(value)); break; - case STRING: - case UNSPECIFIED: { + case props::STRING: + case props::UNSPECIFIED: { char buf[128]; sprintf(buf, "%ld", value); result = set_string(buf); break; } - case NONE: + case props::NONE: default: break; } @@ -1312,44 +1391,44 @@ bool SGPropertyNode::setFloatValue (float value) { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == FLOAT) + if (_attr == (READ|WRITE) && _type == props::FLOAT) return set_float(value); bool result = false; TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = FLOAT; + if (_type == props::NONE || _type == props::UNSPECIFIED) { + clearValue(); + _type = props::FLOAT; _local_val.float_val = 0; } switch (_type) { - case ALIAS: + case props::ALIAS: result = _value.alias->setFloatValue(value); break; - case BOOL: + case props::BOOL: result = set_bool(value == 0.0 ? false : true); break; - case INT: + case props::INT: result = set_int(int(value)); break; - case LONG: + case props::LONG: result = set_long(long(value)); break; - case FLOAT: + case props::FLOAT: result = set_float(value); break; - case DOUBLE: + case props::DOUBLE: result = set_double(double(value)); break; - case STRING: - case UNSPECIFIED: { + case props::STRING: + case props::UNSPECIFIED: { char buf[128]; sprintf(buf, "%f", value); result = set_string(buf); break; } - case NONE: + case props::NONE: default: break; } @@ -1363,44 +1442,44 @@ bool SGPropertyNode::setDoubleValue (double value) { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == DOUBLE) + if (_attr == (READ|WRITE) && _type == props::DOUBLE) return set_double(value); bool result = false; TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); + if (_type == props::NONE || _type == props::UNSPECIFIED) { + clearValue(); _local_val.double_val = value; - _type = DOUBLE; + _type = props::DOUBLE; } switch (_type) { - case ALIAS: + case props::ALIAS: result = _value.alias->setDoubleValue(value); break; - case BOOL: + case props::BOOL: result = set_bool(value == 0.0L ? false : true); break; - case INT: + case props::INT: result = set_int(int(value)); break; - case LONG: + case props::LONG: result = set_long(long(value)); break; - case FLOAT: + case props::FLOAT: result = set_float(float(value)); break; - case DOUBLE: + case props::DOUBLE: result = set_double(value); break; - case STRING: - case UNSPECIFIED: { + case props::STRING: + case props::UNSPECIFIED: { char buf[128]; sprintf(buf, "%f", value); result = set_string(buf); break; } - case NONE: + case props::NONE: default: break; } @@ -1414,41 +1493,47 @@ bool SGPropertyNode::setStringValue (const char * value) { // Shortcut for common case - if (_attr == (READ|WRITE) && _type == STRING) + if (_attr == (READ|WRITE) && _type == props::STRING) return set_string(value); bool result = false; TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = STRING; + if (_type == props::NONE || _type == props::UNSPECIFIED) { + clearValue(); + _type = props::STRING; } switch (_type) { - case ALIAS: + case props::ALIAS: result = _value.alias->setStringValue(value); break; - case BOOL: + case props::BOOL: result = set_bool((compare_strings(value, "true") || atoi(value)) ? true : false); break; - case INT: + case props::INT: result = set_int(atoi(value)); break; - case LONG: + case props::LONG: result = set_long(strtol(value, 0, 0)); break; - case FLOAT: + case props::FLOAT: result = set_float(atof(value)); break; - case DOUBLE: + case props::DOUBLE: result = set_double(strtod(value, 0)); break; - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: result = set_string(value); break; - case NONE: + case props::EXTENDED: + { + stringstream sstr(value); + static_cast(_value.val)->readFrom(sstr); + } + break; + case props::NONE: default: break; } @@ -1463,36 +1548,44 @@ SGPropertyNode::setUnspecifiedValue (const char * value) { bool result = false; TEST_WRITE; - if (_type == NONE) { - clear_value(); - _type = UNSPECIFIED; - } - - switch (_type) { - case ALIAS: + if (_type == props::NONE) { + clearValue(); + _type = props::UNSPECIFIED; + } + props::Type type = _type; + if (type == props::EXTENDED) + type = _value.val->getType(); + switch (type) { + case props::ALIAS: result = _value.alias->setUnspecifiedValue(value); break; - case BOOL: + case props::BOOL: result = set_bool((compare_strings(value, "true") || atoi(value)) ? true : false); break; - case INT: + case props::INT: result = set_int(atoi(value)); break; - case LONG: + case props::LONG: result = set_long(strtol(value, 0, 0)); break; - case FLOAT: + case props::FLOAT: result = set_float(atof(value)); break; - case DOUBLE: + case props::DOUBLE: result = set_double(strtod(value, 0)); break; - case STRING: - case UNSPECIFIED: + case props::STRING: + case props::UNSPECIFIED: result = set_string(value); break; - case NONE: + case props::VEC3D: + result = static_cast*>(_value.val)->setValue(parseString(value)); + break; + case props::VEC4D: + result = static_cast*>(_value.val)->setValue(parseString(value)); + break; + case props::NONE: default: break; } @@ -1502,139 +1595,64 @@ SGPropertyNode::setUnspecifiedValue (const char * value) return result; } -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - 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; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - 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; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - 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; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - 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; - +std::ostream& SGPropertyNode::printOn(std::ostream& stream) const +{ + if (!getAttribute(READ)) + return stream; + switch (_type) { + case props::ALIAS: + return _value.alias->printOn(stream); + case props::BOOL: + stream << (get_bool() ? "true" : "false"); + break; + case props::INT: + stream << get_int(); + break; + case props::LONG: + stream << get_long(); + break; + case props::FLOAT: + stream << get_float(); + break; + case props::DOUBLE: + stream << get_double(); + break; + case props::STRING: + case props::UNSPECIFIED: + stream << get_string(); + break; + case props::EXTENDED: + static_cast(_value.val)->printOn(stream); + break; + case props::NONE: + break; + default: // avoid compiler warning + break; + } + return stream; } -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +template<> +bool SGPropertyNode::tie (const SGRawValue &rawValue, + bool useDefault) { - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - string old_val; - if (useDefault) - old_val = getStringValue(); + if (_type == props::ALIAS || _tied) + return false; - clear_value(); - _type = STRING; - _tied = true; - _value.string_val = rawValue.clone(); + useDefault = useDefault && hasValue(); + std::string old_val; + if (useDefault) + old_val = getStringValue(); + clearValue(); + _type = props::STRING; + _tied = true; + _value.val = rawValue.clone(); - if (useDefault) - setStringValue(old_val.c_str()); + if (useDefault) + setStringValue(old_val.c_str()); - return true; + return true; } - bool SGPropertyNode::untie () { @@ -1642,50 +1660,59 @@ SGPropertyNode::untie () return false; switch (_type) { - case BOOL: { + case props::BOOL: { bool val = getBoolValue(); - clear_value(); - _type = BOOL; + clearValue(); + _type = props::BOOL; _local_val.bool_val = val; break; } - case INT: { + case props::INT: { int val = getIntValue(); - clear_value(); - _type = INT; + clearValue(); + _type = props::INT; _local_val.int_val = val; break; } - case LONG: { + case props::LONG: { long val = getLongValue(); - clear_value(); - _type = LONG; + clearValue(); + _type = props::LONG; _local_val.long_val = val; break; } - case FLOAT: { + case props::FLOAT: { float val = getFloatValue(); - clear_value(); - _type = FLOAT; + clearValue(); + _type = props::FLOAT; _local_val.float_val = val; break; } - case DOUBLE: { + case props::DOUBLE: { double val = getDoubleValue(); - clear_value(); - _type = DOUBLE; + clearValue(); + _type = props::DOUBLE; _local_val.double_val = val; break; } - case STRING: - case UNSPECIFIED: { + case props::STRING: + case props::UNSPECIFIED: { string val = getStringValue(); - clear_value(); - _type = STRING; + clearValue(); + _type = props::STRING; _local_val.string_val = copy_string(val.c_str()); break; } - case NONE: + case props::EXTENDED: { + SGRawExtended* val = static_cast(_value.val); + _value.val = 0; // Prevent clearValue() from deleting + clearValue(); + _type = props::EXTENDED; + _value.val = val->makeContainer(); + delete val; + break; + } + case props::NONE: default: break; } @@ -1715,14 +1742,16 @@ SGPropertyNode::getRootNode () const SGPropertyNode * SGPropertyNode::getNode (const char * relative_path, bool create) { + using namespace boost; if (_path_cache == 0) _path_cache = new hash_table; SGPropertyNode * result = _path_cache->get(relative_path); if (result == 0) { - vector components; - parse_path(relative_path, components); - result = find_node(this, components, 0, create); + result = find_node(this, + make_iterator_range(relative_path, relative_path + + strlen(relative_path)), + create); if (result != 0) _path_cache->put(relative_path, result); } @@ -1733,11 +1762,10 @@ SGPropertyNode::getNode (const char * relative_path, bool create) SGPropertyNode * SGPropertyNode::getNode (const char * relative_path, int index, bool create) { - vector components; - parse_path(relative_path, components); - if (components.size() > 0) - components[components.size()-1].index = index; - return find_node(this, components, 0, create); + using namespace boost; + return find_node(this, make_iterator_range(relative_path, relative_path + + strlen(relative_path)), + create, index); } const SGPropertyNode * @@ -1772,11 +1800,11 @@ SGPropertyNode::hasValue (const char * relative_path) const /** * Get the value type for another node. */ -SGPropertyNode::Type +props::Type SGPropertyNode::getType (const char * relative_path) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? UNSPECIFIED : (Type)(node->getType())); + return (node == 0 ? props::UNSPECIFIED : node->getType()); } @@ -2017,24 +2045,27 @@ SGPropertyNode::untie (const char * relative_path) } void -SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener) +SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener, + bool initial) { if (_listeners == 0) - _listeners = new vector; + _listeners = new vector; _listeners->push_back(listener); listener->register_property(this); + if (initial) + listener->valueChanged(this); } void SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener) { - vector::iterator it = + vector::iterator it = find(_listeners->begin(), _listeners->end(), listener); if (it != _listeners->end()) { _listeners->erase(it); listener->unregister_property(this); if (_listeners->empty()) { - vector * tmp = _listeners; + vector* tmp = _listeners; _listeners = 0; delete tmp; } @@ -2106,8 +2137,7 @@ SGPropertyNode::fireChildRemoved (SGPropertyNode * parent, #define HASH_TABLE_SIZE 199 SGPropertyNode::hash_table::entry::entry () - : _key(0), - _value(0) + : _value(0) { } @@ -2115,13 +2145,12 @@ SGPropertyNode::hash_table::entry::~entry () { // Don't delete the value; we don't own // the pointer. - delete [] _key; } void SGPropertyNode::hash_table::entry::set_key (const char * key) { - _key = copy_string(key); + _key = key; } void @@ -2140,6 +2169,7 @@ SGPropertyNode::hash_table::bucket::~bucket () { for (int i = 0; i < _length; i++) delete _entries[i]; + delete [] _entries; } SGPropertyNode::hash_table::entry * @@ -2166,6 +2196,31 @@ SGPropertyNode::hash_table::bucket::get_entry (const char * key, bool create) } } +bool +SGPropertyNode::hash_table::bucket::erase (SGPropertyNode * node) +{ + for (int i = 0; i < _length; i++) { + if (_entries[i]->get_value() == node) { + delete _entries[i]; + for (++i; i < _length; i++) { + _entries[i-1] = _entries[i]; + } + _length--; + return true; + } + } + return false; +} + +void +SGPropertyNode::hash_table::bucket::clear (SGPropertyNode::hash_table * owner) +{ + for (int i = 0; i < _length; i++) { + SGPropertyNode * node = _entries[i]->get_value(); + if (node) + node->remove_linked_node(owner); + } +} SGPropertyNode::hash_table::hash_table () : _data_length(0), @@ -2175,8 +2230,13 @@ SGPropertyNode::hash_table::hash_table () SGPropertyNode::hash_table::~hash_table () { - for (unsigned int i = 0; i < _data_length; i++) - delete _data[i]; + for (unsigned int i = 0; i < _data_length; i++) { + if (_data[i]) { + _data[i]->clear(this); + delete _data[i]; + } + } + delete [] _data; } SGPropertyNode * @@ -2209,6 +2269,17 @@ SGPropertyNode::hash_table::put (const char * key, SGPropertyNode * value) } entry * e = _data[index]->get_entry(key, true); e->set_value(value); + value->add_linked_node(this); +} + +bool +SGPropertyNode::hash_table::erase (SGPropertyNode * node) +{ + for (unsigned int i = 0; i < _data_length; i++) + if (_data[i] && _data[i]->erase(node)) + return true; + + return false; } unsigned int @@ -2223,103 +2294,6 @@ SGPropertyNode::hash_table::hashcode (const char * key) } - -/** - * Default constructor - */ -SGPropertyNode_ptr::SGPropertyNode_ptr() -{ - _ptr = 0; -} - -/** - * Copy constructor - */ -SGPropertyNode_ptr::SGPropertyNode_ptr( const SGPropertyNode_ptr &r ) -{ - _ptr = r._ptr; - if (_ptr) - _ptr->incrementRef(); -} - -/** - * Constructor from a pointer to a node - */ -SGPropertyNode_ptr::SGPropertyNode_ptr( SGPropertyNode *p ) -{ - _ptr = p; - if (_ptr) - _ptr->incrementRef(); -} - -/** - * Destructor - */ -SGPropertyNode_ptr::~SGPropertyNode_ptr() -{ - if (_ptr && _ptr->decrementRef() == 0) - delete _ptr; -} - -/** - * Assignement operator - */ -SGPropertyNode_ptr & -SGPropertyNode_ptr::operator=( const SGPropertyNode_ptr &r ) -{ - if (_ptr && _ptr->decrementRef() == 0) - delete _ptr; - _ptr = r._ptr; - if (_ptr) - _ptr->incrementRef(); - - return *this; -} - -/** - * Pointer access operator - */ -SGPropertyNode * -SGPropertyNode_ptr::operator->() -{ - return _ptr; -} - -/** - * Pointer access operator (const) - */ -const SGPropertyNode * -SGPropertyNode_ptr::operator->() const -{ - return _ptr; -} - -/** - * Conversion to SGPropertyNode * operator - */ -SGPropertyNode_ptr::operator SGPropertyNode *() -{ - return _ptr; -} - -/** - * Conversion to const SGPropertyNode * operator - */ -SGPropertyNode_ptr::operator const SGPropertyNode *() const -{ - return _ptr; -} - -/** - * Validity test - */ -bool -SGPropertyNode_ptr::valid() const -{ - return _ptr != 0; -} - - //////////////////////////////////////////////////////////////////////// // Implementation of SGPropertyChangeListener. @@ -2327,12 +2301,8 @@ SGPropertyNode_ptr::valid() const SGPropertyChangeListener::~SGPropertyChangeListener () { - // This will come back and remove - // the current item each time. Is - // that OK? - vector::iterator it; - for (it = _properties.begin(); it != _properties.end(); it++) - (*it)->removeChangeListener(this); + for (int i = _properties.size() - 1; i >= 0; i--) + _properties[i]->removeChangeListener(this); } void @@ -2370,5 +2340,193 @@ SGPropertyChangeListener::unregister_property (SGPropertyNode * node) _properties.erase(it); } +template<> +std::ostream& SGRawBase::printOn(std::ostream& stream) const +{ + const SGVec3d vec + = static_cast*>(this)->getValue(); + for (int i = 0; i < 3; ++i) { + stream << vec[i]; + if (i < 2) + stream << ' '; + } + return stream; +} + +namespace simgear +{ +template<> +std::istream& readFrom(std::istream& stream, SGVec3d& result) +{ + for (int i = 0; i < 3; ++i) { + stream >> result[i]; + } + return stream; +} +} +template<> +std::ostream& SGRawBase::printOn(std::ostream& stream) const +{ + const SGVec4d vec + = static_cast*>(this)->getValue(); + for (int i = 0; i < 4; ++i) { + stream << vec[i]; + if (i < 3) + stream << ' '; + } + return stream; +} + +namespace simgear +{ +template<> +std::istream& readFrom(std::istream& stream, SGVec4d& result) +{ + for (int i = 0; i < 4; ++i) { + stream >> result[i]; + } + return stream; +} + +namespace +{ +bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) +{ + props::Type ltype = lhs.getType(); + props::Type rtype = rhs.getType(); + if (ltype != rtype) + return false; + switch (ltype) { + case props::NONE: + return true; + case props::ALIAS: + return false; // XXX Should we look in aliases? + case props::BOOL: + return lhs.getValue() == rhs.getValue(); + case props::INT: + return lhs.getValue() == rhs.getValue(); + case props::LONG: + return lhs.getValue() == rhs.getValue(); + case props::FLOAT: + return lhs.getValue() == rhs.getValue(); + case props::DOUBLE: + return lhs.getValue() == rhs.getValue(); + case props::STRING: + case props::UNSPECIFIED: + return !strcmp(lhs.getStringValue(), rhs.getStringValue()); + case props::VEC3D: + return lhs.getValue() == rhs.getValue(); + case props::VEC4D: + return lhs.getValue() == rhs.getValue(); + default: + return false; + } +} +} +} + +bool SGPropertyNode::compare(const SGPropertyNode& lhs, + const SGPropertyNode& rhs) +{ + if (&lhs == &rhs) + return true; + int lhsChildren = lhs.nChildren(); + int rhsChildren = rhs.nChildren(); + if (lhsChildren != rhsChildren) + return false; + if (lhsChildren == 0) + return compareNodeValue(lhs, rhs); + for (size_t i = 0; i < lhs._children.size(); ++i) { + const SGPropertyNode* lchild = lhs._children[i]; + const SGPropertyNode* rchild = rhs._children[i]; + // I'm guessing that the nodes will usually be in the same + // order. + if (lchild->getIndex() != rchild->getIndex() + || lchild->getNameString() != rchild->getNameString()) { + rchild = 0; + for (PropertyList::const_iterator itr = rhs._children.begin(), + end = rhs._children.end(); + itr != end; + ++itr) + if (lchild->getIndex() == (*itr)->getIndex() + && lchild->getNameString() == (*itr)->getNameString()) { + rchild = *itr; + break; + } + if (!rchild) + return false; + } + if (!compare(*lchild, *rchild)) + return false; + } + return true; +} + +struct PropertyPlaceLess { + typedef bool result_type; + bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const + { + int comp = lhs->getNameString().compare(rhs->getNameString()); + if (comp == 0) + return lhs->getIndex() < rhs->getIndex(); + else + return comp < 0; + } +}; + +size_t hash_value(const SGPropertyNode& node) +{ + using namespace boost; + + if (node.nChildren() == 0) { + switch (node.getType()) { + case props::NONE: + return 0; + + case props::BOOL: + return hash_value(node.getValue()); + case props::INT: + return hash_value(node.getValue()); + case props::LONG: + return hash_value(node.getValue()); + case props::FLOAT: + return hash_value(node.getValue()); + case props::DOUBLE: + return hash_value(node.getValue()); + case props::STRING: + case props::UNSPECIFIED: + { + const char *val = node.getStringValue(); + return hash_range(val, val + strlen(val)); + } + case props::VEC3D: + { + const SGVec3d val = node.getValue(); + return hash_range(&val[0], &val[3]); + } + case props::VEC4D: + { + const SGVec4d val = node.getValue(); + return hash_range(&val[0], &val[4]); + } + case props::ALIAS: // XXX Should we look in aliases? + default: + return 0; + } + } else { + size_t seed = 0; + PropertyList children(node._children.begin(), node._children.end()); + sort(children.begin(), children.end(), PropertyPlaceLess()); + for (PropertyList::const_iterator itr = children.begin(), + end = children.end(); + itr != end; + ++itr) { + hash_combine(seed, (*itr)->_name); + hash_combine(seed, (*itr)->_index); + hash_combine(seed, hash_value(**itr)); + } + return seed; + } +} // end of props.cxx