1 // props.cxx - implementation of a property list.
2 // Started Fall 2000 by David Megginson, david@megginson.com
3 // This code is released into the Public Domain.
5 // See props.html for documentation [replace with URL when available].
10 # include <simgear_config.h>
29 # include <boost/algorithm/string/find_iterator.hpp>
30 # include <boost/algorithm/string/predicate.hpp>
31 # include <boost/algorithm/string/classification.hpp>
32 # include <boost/bind.hpp>
33 # include <boost/functional/hash.hpp>
34 # include <boost/range.hpp>
35 # include <simgear/compiler.h>
36 # include <simgear/debug/logstream.hxx>
38 # include "PropertyInterpolationMgr.hxx"
39 # include "vectorPropTemplates.hxx"
41 # if ( _MSC_VER == 1200 )
42 // MSVC 6 is buggy, and needs something strange here
43 using std::vector<SGPropertyNode_ptr>;
44 using std::vector<SGPropertyChangeListener *>;
45 using std::vector<SGPropertyNode *>;
53 using std::stringstream;
55 using namespace simgear;
57 ////////////////////////////////////////////////////////////////////////
59 ////////////////////////////////////////////////////////////////////////
62 * Comparator class for sorting by index.
67 int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
68 return (n1->getIndex() < n2->getIndex());
73 ////////////////////////////////////////////////////////////////////////
74 // Convenience macros for value access.
75 ////////////////////////////////////////////////////////////////////////
77 #define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
78 #define TEST_WRITE if (!getAttribute(WRITE)) return false
80 ////////////////////////////////////////////////////////////////////////
81 // Local path normalization code.
82 ////////////////////////////////////////////////////////////////////////
93 * Parse the name for a path component.
95 * Name: [_a-zA-Z][-._a-zA-Z0-9]*
98 template<typename Range>
100 parse_name (const SGPropertyNode *node, const Range &path)
102 typename Range::iterator i = path.begin();
103 typename Range::iterator max = path.end();
107 if (i != path.end() && *i == '.') {
110 if (i != max && *i != '/')
111 throw std::string("illegal character after . or ..");
112 } else if (isalpha(*i) || *i == '_') {
115 // The rules inside a name are a little
118 if (isalpha(*i) || isdigit(*i) || *i == '_' ||
119 *i == '-' || *i == '.') {
121 } else if (*i == '[' || *i == '/') {
124 std::string err = "'";
126 err.append("' found in propertyname after '"+node->getNameString()+"'");
127 err.append("\nname may contain only ._- and alphanumeric characters");
135 if (path.begin() == i) {
136 std::string err = "'";
138 err.append("' found in propertyname after '"+node->getNameString()+"'");
139 err.append("\nname must begin with alpha or '_'");
143 return Range(path.begin(), i);
146 // Validate the name of a single node
147 inline bool validateName(const std::string& name)
149 using namespace boost;
152 if (!isalpha(name[0]) && name[0] != '_')
155 std::string is_any_of("_-.");
157 for(unsigned i=1; i<name.length(); ++i) {
158 if (!isalnum(name[i]) && is_any_of.find(name[i]) == std::string::npos) {
165 return all(make_iterator_range(name.begin(), name.end()),
166 is_alnum() || is_any_of("_-."));
172 * Parse the name for a path component.
174 * Name: [_a-zA-Z][-._a-zA-Z0-9]*
176 static inline const string
177 parse_name (const string &path, int &i)
180 int max = (int)path.size();
182 if (path[i] == '.') {
184 if (i < max && path[i] == '.') {
190 if (i < max && path[i] != '/')
191 throw string("Illegal character after " + name);
194 else if (isalpha(path[i]) || path[i] == '_') {
198 // The rules inside a name are a little
201 if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
202 path[i] == '-' || path[i] == '.') {
204 } else if (path[i] == '[' || path[i] == '/') {
207 throw string("name may contain only ._- and alphanumeric characters");
214 if (name.size() == 0)
215 throw string("name must begin with alpha or '_'");
223 * Parse the optional integer index for a path component.
225 * Index: "[" [0-9]+ "]"
228 parse_index (const string &path, int &i)
237 for (int max = (int)path.size(); i < max; i++) {
238 if (isdigit(path[i])) {
239 index = (index * 10) + (path[i] - '0');
240 } else if (path[i] == ']') {
248 throw string("unterminated index (looking for ']')");
252 * Parse a single path component.
254 * Component: Name Index?
256 static inline PathComponent
257 parse_component (const string &path, int &i)
259 PathComponent component;
260 component.name = parse_name(path, i);
261 if (component.name[0] != '.')
262 component.index = parse_index(path, i);
264 component.index = -1;
269 * Parse a path into its components.
272 parse_path (const string &path, vector<PathComponent> &components)
275 int max = (int)path.size();
277 // Check for initial '/'
278 if (path[pos] == '/') {
282 components.push_back(root);
284 while (pos < max && path[pos] == '/')
289 components.push_back(parse_component(path, pos));
290 while (pos < max && path[pos] == '/')
297 ////////////////////////////////////////////////////////////////////////
298 // Other static utility functions.
299 ////////////////////////////////////////////////////////////////////////
303 copy_string (const char * s)
305 size_t slen = strlen(s);
306 char * copy = new char[slen + 1];
308 // the source string length is known so no need to check for '\0'
309 // when copying every single character
310 memcpy(copy, s, slen);
311 *(copy + slen) = '\0';
316 compare_strings (const char * s1, const char * s2)
318 return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
322 * Locate a child node by name and index.
324 template<typename Itr>
326 find_child (Itr begin, Itr end, int index, const PropertyList& nodes)
328 size_t nNodes = nodes.size();
330 for (int i = 0; i < nNodes; i++) {
331 SGPropertyNode * node = nodes[i];
332 if (node->getIndex() == index && compare_strings(node->getName(), begin))
336 boost::iterator_range<Itr> name(begin, end);
337 for (size_t i = 0; i < nNodes; i++) {
338 SGPropertyNode * node = nodes[i];
340 // searching for a matching index is a lot less time consuming than
341 // comparing two strings so do that first.
342 if (node->getIndex() == index && boost::equals(node->getName(), name))
343 return static_cast<int>(i);
350 * Locate the child node with the highest index of the same name
353 find_last_child (const char * name, const PropertyList& nodes)
355 size_t nNodes = nodes.size();
358 for (size_t i = 0; i < nNodes; i++) {
359 SGPropertyNode * node = nodes[i];
360 if (compare_strings(node->getName(), name))
362 int idx = node->getIndex();
363 if (idx > index) index = idx;
370 * Get first unused index for child nodes with the given name
373 first_unused_index( const char * name,
374 const PropertyList& nodes,
377 const char* nameEnd = name + strlen(name);
379 for( int index = min_index; index < std::numeric_limits<int>::max(); ++index )
381 if( find_child(name, nameEnd, index, nodes) < 0 )
385 SG_LOG(SG_GENERAL, SG_ALERT, "Too many nodes: " << name);
389 template<typename Itr>
390 inline SGPropertyNode*
391 SGPropertyNode::getExistingChild (Itr begin, Itr end, int index)
393 int pos = find_child(begin, end, index, _children);
395 return _children[pos];
399 template<typename Itr>
401 SGPropertyNode::getChildImpl (Itr begin, Itr end, int index, bool create)
403 SGPropertyNode* node = getExistingChild(begin, end, index);
408 node = new SGPropertyNode(begin, end, index, this);
409 _children.push_back(node);
410 fireChildAdded(node);
417 template<typename SplitItr>
419 find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create,
422 typedef typename SplitItr::value_type Range;
423 // Run off the end of the list
428 // Success! This is the one we want.
432 // Empty name at this point is empty, not root.
434 return find_node_aux(current, ++itr, create, last_index);
435 Range name = parse_name(current, token);
436 if (equals(name, "."))
437 return find_node_aux(current, ++itr, create, last_index);
438 if (equals(name, "..")) {
439 SGPropertyNode * parent = current->getParent();
441 throw std::string("attempt to move past root with '..'");
442 return find_node_aux(parent, ++itr, create, last_index);
445 if (last_index >= 0) {
446 // If we are at the last token and last_index is valid, use
447 // last_index as the index value
449 while (!(++itr).eof()) {
463 if (name.end() != token.end()) {
464 if (*name.end() == '[') {
465 typename Range::iterator i = name.end() + 1, end = token.end();
466 for (;i != end; ++i) {
468 index = (index * 10) + (*i - '0');
473 if (i == token.end() || *i != ']')
474 throw std::string("unterminated index (looking for ']')");
476 throw std::string("illegal characters in token: ")
477 + std::string(name.begin(), name.end());
481 return find_node_aux(current->getChildImpl(name.begin(), name.end(),
482 index, create), itr, create,
486 // Internal function for parsing property paths. last_index provides
487 // and index value for the last node name token, if supplied.
489 static SGPropertyNode *
490 find_node (SGPropertyNode * current,
491 const vector<PathComponent> &components,
495 // Run off the end of the list
500 // Success! This is the one we want.
501 else if (position >= (int)components.size()) {
502 return (current->getAttribute(SGPropertyNode::REMOVED) ? 0 : current);
505 // Empty component means root.
506 else if (components[position].name == "") {
507 return find_node(current->getRootNode(), components, position + 1, create);
510 // . means current directory
511 else if (components[position].name == ".") {
512 return find_node(current, components, position + 1, create);
515 // .. means parent directory
516 else if (components[position].name == "..") {
517 SGPropertyNode * parent = current->getParent();
519 throw string("Attempt to move past root with '..'");
521 return find_node(parent, components, position + 1, create);
524 // Otherwise, a child name
526 SGPropertyNode * child =
527 current->getChild(components[position].name.c_str(),
528 components[position].index,
530 return find_node(child, components, position + 1, create);
534 template<typename Range>
536 find_node (SGPropertyNode * current,
541 using namespace boost;
542 typedef split_iterator<typename range_result_iterator<Range>::type>
545 PathSplitIterator itr
546 = make_split_iterator(path, first_finder("/", is_equal()));
547 if (*path.begin() == '/')
548 return find_node_aux(current->getRootNode(), itr, create, last_index);
550 return find_node_aux(current, itr, create, last_index);
554 ////////////////////////////////////////////////////////////////////////
555 // Private methods from SGPropertyNode (may be inlined for speed).
556 ////////////////////////////////////////////////////////////////////////
559 SGPropertyNode::get_bool () const
562 return static_cast<SGRawValue<bool>*>(_value.val)->getValue();
564 return _local_val.bool_val;
568 SGPropertyNode::get_int () const
571 return (static_cast<SGRawValue<int>*>(_value.val))->getValue();
573 return _local_val.int_val;
577 SGPropertyNode::get_long () const
580 return static_cast<SGRawValue<long>*>(_value.val)->getValue();
582 return _local_val.long_val;
586 SGPropertyNode::get_float () const
589 return static_cast<SGRawValue<float>*>(_value.val)->getValue();
591 return _local_val.float_val;
595 SGPropertyNode::get_double () const
598 return static_cast<SGRawValue<double>*>(_value.val)->getValue();
600 return _local_val.double_val;
604 SGPropertyNode::get_string () const
607 return static_cast<SGRawValue<const char*>*>(_value.val)->getValue();
609 return _local_val.string_val;
613 SGPropertyNode::set_bool (bool val)
616 if (static_cast<SGRawValue<bool>*>(_value.val)->setValue(val)) {
623 _local_val.bool_val = val;
630 SGPropertyNode::set_int (int val)
633 if (static_cast<SGRawValue<int>*>(_value.val)->setValue(val)) {
640 _local_val.int_val = val;
647 SGPropertyNode::set_long (long val)
650 if (static_cast<SGRawValue<long>*>(_value.val)->setValue(val)) {
657 _local_val.long_val = val;
664 SGPropertyNode::set_float (float val)
667 if (static_cast<SGRawValue<float>*>(_value.val)->setValue(val)) {
674 _local_val.float_val = val;
681 SGPropertyNode::set_double (double val)
684 if (static_cast<SGRawValue<double>*>(_value.val)->setValue(val)) {
691 _local_val.double_val = val;
698 SGPropertyNode::set_string (const char * val)
701 if (static_cast<SGRawValue<const char*>*>(_value.val)->setValue(val)) {
708 delete [] _local_val.string_val;
709 _local_val.string_val = copy_string(val);
716 SGPropertyNode::clearValue ()
718 if (_type == props::ALIAS) {
721 } else if (_type != props::NONE) {
724 _local_val.bool_val = SGRawValue<bool>::DefaultValue();
727 _local_val.int_val = SGRawValue<int>::DefaultValue();
730 _local_val.long_val = SGRawValue<long>::DefaultValue();
733 _local_val.float_val = SGRawValue<float>::DefaultValue();
736 _local_val.double_val = SGRawValue<double>::DefaultValue();
739 case props::UNSPECIFIED:
741 delete [] _local_val.string_val;
743 _local_val.string_val = 0;
745 default: // avoid compiler warning
757 * Get the value as a string.
760 SGPropertyNode::make_string () const
762 if (!getAttribute(READ))
766 return _value.alias->getStringValue();
768 return get_bool() ? "true" : "false";
770 case props::UNSPECIFIED:
789 sstr << std::setprecision(10) << get_double();
791 case props::EXTENDED:
793 props::Type realType = _value.val->getType();
794 // Perhaps this should be done for all types?
795 if (realType == props::VEC3D || realType == props::VEC4D)
797 static_cast<SGRawExtended*>(_value.val)->printOn(sstr);
803 _buffer = sstr.str();
804 return _buffer.c_str();
808 * Trace a write access for a property.
811 SGPropertyNode::trace_write () const
813 SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << getPath()
814 << ", value \"" << make_string() << '"');
818 * Trace a read access for a property.
821 SGPropertyNode::trace_read () const
823 SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << getPath()
824 << ", value \"" << make_string() << '"');
827 ////////////////////////////////////////////////////////////////////////
828 // Public methods from SGPropertyNode.
829 ////////////////////////////////////////////////////////////////////////
832 * Last used attribute
833 * Update as needed when enum Attribute is changed
835 const int SGPropertyNode::LAST_USED_ATTRIBUTE = PRESERVE;
838 * Default constructor: always creates a root node.
840 SGPropertyNode::SGPropertyNode ()
848 _local_val.string_val = 0;
856 SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
857 : SGReferenced(node),
860 _parent(0), // don't copy the parent
864 _listeners(0) // CHECK!!
866 _local_val.string_val = 0;
868 if (_type == props::NONE)
870 if (_type == props::ALIAS) {
871 _value.alias = node._value.alias;
876 if (_tied || _type == props::EXTENDED) {
877 _value.val = node._value.val->clone();
882 set_bool(node.get_bool());
885 set_int(node.get_int());
888 set_long(node.get_long());
891 set_float(node.get_float());
894 set_double(node.get_double());
897 case props::UNSPECIFIED:
898 set_string(node.get_string());
907 * Convenience constructor.
909 template<typename Itr>
910 SGPropertyNode::SGPropertyNode (Itr begin, Itr end,
912 SGPropertyNode * parent)
921 _local_val.string_val = 0;
923 if (!validateName(_name))
924 throw std::string("plain name expected instead of '") + _name + '\'';
927 SGPropertyNode::SGPropertyNode( const std::string& name,
929 SGPropertyNode * parent)
938 _local_val.string_val = 0;
940 if (!validateName(name))
941 throw std::string("plain name expected instead of '") + _name + '\'';
947 SGPropertyNode::~SGPropertyNode ()
949 // zero out all parent pointers, else they might be dangling
950 for (unsigned i = 0; i < _children.size(); ++i)
951 _children[i]->_parent = 0;
955 vector<SGPropertyChangeListener*>::iterator it;
956 for (it = _listeners->begin(); it != _listeners->end(); ++it)
957 (*it)->unregister_property(this);
964 * Alias to another node.
967 SGPropertyNode::alias (SGPropertyNode * target)
969 if (target && (_type != props::ALIAS) && (!_tied))
973 _value.alias = target;
974 _type = props::ALIAS;
980 SG_LOG(SG_GENERAL, SG_ALERT,
981 "Failed to create alias for " << getPath() << ". "
982 "The target property does not exist.");
985 if (_type == props::ALIAS)
987 if (_value.alias == target)
988 return true; // ok, identical alias requested
989 SG_LOG(SG_GENERAL, SG_ALERT,
990 "Failed to create alias at " << target->getPath() << ". "
991 "Source "<< getPath() << " is already aliasing another property.");
996 SG_LOG(SG_GENERAL, SG_ALERT, "Failed to create alias at " << target->getPath() << ". "
997 "Source " << getPath() << " is a tied property.");
1005 * Alias to another node by path.
1008 SGPropertyNode::alias (const char * path)
1010 return alias(getNode(path, true));
1018 SGPropertyNode::unalias ()
1020 if (_type != props::ALIAS)
1028 * Get the target of an alias.
1031 SGPropertyNode::getAliasTarget ()
1033 return (_type == props::ALIAS ? _value.alias : 0);
1037 const SGPropertyNode *
1038 SGPropertyNode::getAliasTarget () const
1040 return (_type == props::ALIAS ? _value.alias : 0);
1044 * create a non-const child by name after the last node with the same name.
1047 SGPropertyNode::addChild(const char * name, int min_index, bool append)
1050 ? std::max(find_last_child(name, _children) + 1, min_index)
1051 : first_unused_index(name, _children, min_index);
1053 SGPropertyNode_ptr node;
1054 node = new SGPropertyNode(name, name + strlen(name), pos, this);
1055 _children.push_back(node);
1056 fireChildAdded(node);
1061 * Create multiple children with unused indices
1063 simgear::PropertyList
1064 SGPropertyNode::addChildren( const std::string& name,
1069 simgear::PropertyList nodes;
1070 std::set<int> used_indices;
1074 // First grab all used indices. This saves us of testing every index if it
1075 // is used for every element to be created
1076 for( size_t i = 0; i < nodes.size(); i++ )
1078 const SGPropertyNode* node = nodes[i];
1080 if( node->getNameString() == name && node->getIndex() >= min_index )
1081 used_indices.insert(node->getIndex());
1086 // If we don't want to fill the holes just find last node
1087 min_index = std::max(find_last_child(name.c_str(), _children) + 1, min_index);
1090 for( int index = min_index;
1091 index < std::numeric_limits<int>::max() && nodes.size() < count;
1094 if( used_indices.find(index) == used_indices.end() )
1096 SGPropertyNode_ptr node;
1097 node = new SGPropertyNode(name, index, this);
1098 _children.push_back(node);
1099 fireChildAdded(node);
1100 nodes.push_back(node);
1108 * Get a non-const child by index.
1111 SGPropertyNode::getChild (int position)
1113 if (position >= 0 && position < nChildren())
1114 return _children[position];
1121 * Get a const child by index.
1123 const SGPropertyNode *
1124 SGPropertyNode::getChild (int position) const
1126 if (position >= 0 && position < nChildren())
1127 return _children[position];
1134 * Get a non-const child by name and index, creating if necessary.
1138 SGPropertyNode::getChild (const char * name, int index, bool create)
1140 return getChildImpl(name, name + strlen(name), index, create);
1144 SGPropertyNode::getChild (const std::string& name, int index, bool create)
1146 #if PROPS_STANDALONE
1147 const char *n = name.c_str();
1148 int pos = find_child(n, n + strlen(n), index, _children);
1150 return _children[pos];
1152 SGPropertyNode* node = getExistingChild(name.begin(), name.end(), index);
1156 } else if (create) {
1157 SGPropertyNode* node = new SGPropertyNode(name, index, this);
1158 _children.push_back(node);
1159 fireChildAdded(node);
1167 * Get a const child by name and index.
1169 const SGPropertyNode *
1170 SGPropertyNode::getChild (const char * name, int index) const
1172 int pos = find_child(name, name + strlen(name), index, _children);
1174 return _children[pos];
1181 * Get all children with the same name (but different indices).
1184 SGPropertyNode::getChildren (const char * name) const
1186 PropertyList children;
1187 size_t max = _children.size();
1189 for (size_t i = 0; i < max; i++)
1190 if (compare_strings(_children[i]->getName(), name))
1191 children.push_back(_children[i]);
1193 sort(children.begin(), children.end(), CompareIndices());
1197 //------------------------------------------------------------------------------
1198 bool SGPropertyNode::removeChild(SGPropertyNode* node)
1200 if( node->_parent != this )
1203 PropertyList::iterator it =
1204 std::find(_children.begin(), _children.end(), node);
1205 if( it == _children.end() )
1212 //------------------------------------------------------------------------------
1213 SGPropertyNode_ptr SGPropertyNode::removeChild(int pos)
1215 if (pos < 0 || pos >= (int)_children.size())
1216 return SGPropertyNode_ptr();
1218 return eraseChild(_children.begin() + pos);
1223 * Remove a child node
1226 SGPropertyNode::removeChild(const char * name, int index)
1228 SGPropertyNode_ptr ret;
1229 int pos = find_child(name, name + strlen(name), index, _children);
1231 ret = removeChild(pos);
1237 * Remove all children with the specified name.
1240 SGPropertyNode::removeChildren(const char * name)
1242 PropertyList children;
1244 for (int pos = static_cast<int>(_children.size() - 1); pos >= 0; pos--)
1245 if (compare_strings(_children[pos]->getName(), name))
1246 children.push_back(removeChild(pos));
1248 sort(children.begin(), children.end(), CompareIndices());
1253 SGPropertyNode::removeAllChildren()
1255 for(unsigned i = 0; i < _children.size(); ++i)
1257 SGPropertyNode_ptr& node = _children[i];
1259 node->setAttribute(REMOVED, true);
1261 fireChildRemoved(node);
1268 SGPropertyNode::getDisplayName (bool simplify) const
1270 std::string display_name = _name;
1271 if (_index != 0 || !simplify) {
1273 sstr << '[' << _index << ']';
1274 display_name += sstr.str();
1276 return display_name;
1280 SGPropertyNode::getPath (bool simplify) const
1282 typedef std::vector<SGConstPropertyNode_ptr> PList;
1284 for (const SGPropertyNode* node = this; node->_parent; node = node->_parent)
1285 pathList.push_back(node);
1287 for (PList::reverse_iterator itr = pathList.rbegin(),
1288 rend = pathList.rend();
1292 result += (*itr)->getDisplayName(simplify);
1298 SGPropertyNode::getType () const
1300 if (_type == props::ALIAS)
1301 return _value.alias->getType();
1302 else if (_type == props::EXTENDED)
1303 return _value.val->getType();
1310 SGPropertyNode::getBoolValue () const
1312 // Shortcut for common case
1313 if (_attr == (READ|WRITE) && _type == props::BOOL)
1316 if (getAttribute(TRACE_READ))
1318 if (!getAttribute(READ))
1319 return SGRawValue<bool>::DefaultValue();
1322 return _value.alias->getBoolValue();
1326 return get_int() == 0 ? false : true;
1328 return get_long() == 0L ? false : true;
1330 return get_float() == 0.0 ? false : true;
1332 return get_double() == 0.0L ? false : true;
1334 case props::UNSPECIFIED:
1335 return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
1338 return SGRawValue<bool>::DefaultValue();
1343 SGPropertyNode::getIntValue () const
1345 // Shortcut for common case
1346 if (_attr == (READ|WRITE) && _type == props::INT)
1349 if (getAttribute(TRACE_READ))
1351 if (!getAttribute(READ))
1352 return SGRawValue<int>::DefaultValue();
1355 return _value.alias->getIntValue();
1357 return int(get_bool());
1361 return int(get_long());
1363 return int(get_float());
1365 return int(get_double());
1367 case props::UNSPECIFIED:
1368 return atoi(get_string());
1371 return SGRawValue<int>::DefaultValue();
1376 SGPropertyNode::getLongValue () const
1378 // Shortcut for common case
1379 if (_attr == (READ|WRITE) && _type == props::LONG)
1382 if (getAttribute(TRACE_READ))
1384 if (!getAttribute(READ))
1385 return SGRawValue<long>::DefaultValue();
1388 return _value.alias->getLongValue();
1390 return long(get_bool());
1392 return long(get_int());
1396 return long(get_float());
1398 return long(get_double());
1400 case props::UNSPECIFIED:
1401 return strtol(get_string(), 0, 0);
1404 return SGRawValue<long>::DefaultValue();
1409 SGPropertyNode::getFloatValue () const
1411 // Shortcut for common case
1412 if (_attr == (READ|WRITE) && _type == props::FLOAT)
1415 if (getAttribute(TRACE_READ))
1417 if (!getAttribute(READ))
1418 return SGRawValue<float>::DefaultValue();
1421 return _value.alias->getFloatValue();
1423 return float(get_bool());
1425 return float(get_int());
1427 return float(get_long());
1431 return float(get_double());
1433 case props::UNSPECIFIED:
1434 return atof(get_string());
1437 return SGRawValue<float>::DefaultValue();
1442 SGPropertyNode::getDoubleValue () const
1444 // Shortcut for common case
1445 if (_attr == (READ|WRITE) && _type == props::DOUBLE)
1446 return get_double();
1448 if (getAttribute(TRACE_READ))
1450 if (!getAttribute(READ))
1451 return SGRawValue<double>::DefaultValue();
1455 return _value.alias->getDoubleValue();
1457 return double(get_bool());
1459 return double(get_int());
1461 return double(get_long());
1463 return double(get_float());
1465 return get_double();
1467 case props::UNSPECIFIED:
1468 return strtod(get_string(), 0);
1471 return SGRawValue<double>::DefaultValue();
1476 SGPropertyNode::getStringValue () const
1478 // Shortcut for common case
1479 if (_attr == (READ|WRITE) && _type == props::STRING)
1480 return get_string();
1482 if (getAttribute(TRACE_READ))
1484 if (!getAttribute(READ))
1485 return SGRawValue<const char *>::DefaultValue();
1486 return make_string();
1490 SGPropertyNode::setBoolValue (bool value)
1492 // Shortcut for common case
1493 if (_attr == (READ|WRITE) && _type == props::BOOL)
1494 return set_bool(value);
1496 bool result = false;
1498 if (_type == props::NONE || _type == props::UNSPECIFIED) {
1501 _type = props::BOOL;
1506 result = _value.alias->setBoolValue(value);
1509 result = set_bool(value);
1512 result = set_int(int(value));
1515 result = set_long(long(value));
1518 result = set_float(float(value));
1521 result = set_double(double(value));
1524 case props::UNSPECIFIED:
1525 result = set_string(value ? "true" : "false");
1532 if (getAttribute(TRACE_WRITE))
1538 SGPropertyNode::setIntValue (int value)
1540 // Shortcut for common case
1541 if (_attr == (READ|WRITE) && _type == props::INT)
1542 return set_int(value);
1544 bool result = false;
1546 if (_type == props::NONE || _type == props::UNSPECIFIED) {
1549 _local_val.int_val = 0;
1554 result = _value.alias->setIntValue(value);
1557 result = set_bool(value == 0 ? false : true);
1560 result = set_int(value);
1563 result = set_long(long(value));
1566 result = set_float(float(value));
1569 result = set_double(double(value));
1572 case props::UNSPECIFIED: {
1574 sprintf(buf, "%d", value);
1575 result = set_string(buf);
1583 if (getAttribute(TRACE_WRITE))
1589 SGPropertyNode::setLongValue (long value)
1591 // Shortcut for common case
1592 if (_attr == (READ|WRITE) && _type == props::LONG)
1593 return set_long(value);
1595 bool result = false;
1597 if (_type == props::NONE || _type == props::UNSPECIFIED) {
1599 _type = props::LONG;
1600 _local_val.long_val = 0L;
1605 result = _value.alias->setLongValue(value);
1608 result = set_bool(value == 0L ? false : true);
1611 result = set_int(int(value));
1614 result = set_long(value);
1617 result = set_float(float(value));
1620 result = set_double(double(value));
1623 case props::UNSPECIFIED: {
1625 sprintf(buf, "%ld", value);
1626 result = set_string(buf);
1634 if (getAttribute(TRACE_WRITE))
1640 SGPropertyNode::setFloatValue (float value)
1642 // Shortcut for common case
1643 if (_attr == (READ|WRITE) && _type == props::FLOAT)
1644 return set_float(value);
1646 bool result = false;
1648 if (_type == props::NONE || _type == props::UNSPECIFIED) {
1650 _type = props::FLOAT;
1651 _local_val.float_val = 0;
1656 result = _value.alias->setFloatValue(value);
1659 result = set_bool(value == 0.0 ? false : true);
1662 result = set_int(int(value));
1665 result = set_long(long(value));
1668 result = set_float(value);
1671 result = set_double(double(value));
1674 case props::UNSPECIFIED: {
1676 sprintf(buf, "%f", value);
1677 result = set_string(buf);
1685 if (getAttribute(TRACE_WRITE))
1691 SGPropertyNode::setDoubleValue (double value)
1693 // Shortcut for common case
1694 if (_attr == (READ|WRITE) && _type == props::DOUBLE)
1695 return set_double(value);
1697 bool result = false;
1699 if (_type == props::NONE || _type == props::UNSPECIFIED) {
1701 _local_val.double_val = value;
1702 _type = props::DOUBLE;
1707 result = _value.alias->setDoubleValue(value);
1710 result = set_bool(value == 0.0L ? false : true);
1713 result = set_int(int(value));
1716 result = set_long(long(value));
1719 result = set_float(float(value));
1722 result = set_double(value);
1725 case props::UNSPECIFIED: {
1727 sprintf(buf, "%f", value);
1728 result = set_string(buf);
1736 if (getAttribute(TRACE_WRITE))
1742 SGPropertyNode::setStringValue (const char * value)
1744 // Shortcut for common case
1745 if (_attr == (READ|WRITE) && _type == props::STRING)
1746 return set_string(value);
1748 bool result = false;
1750 if (_type == props::NONE || _type == props::UNSPECIFIED) {
1752 _type = props::STRING;
1757 result = _value.alias->setStringValue(value);
1760 result = set_bool((compare_strings(value, "true")
1761 || atoi(value)) ? true : false);
1764 result = set_int(atoi(value));
1767 result = set_long(strtol(value, 0, 0));
1770 result = set_float(atof(value));
1773 result = set_double(strtod(value, 0));
1776 case props::UNSPECIFIED:
1777 result = set_string(value);
1779 case props::EXTENDED:
1781 stringstream sstr(value);
1782 static_cast<SGRawExtended*>(_value.val)->readFrom(sstr);
1790 if (getAttribute(TRACE_WRITE))
1796 SGPropertyNode::setUnspecifiedValue (const char * value)
1798 bool result = false;
1800 if (_type == props::NONE) {
1802 _type = props::UNSPECIFIED;
1804 props::Type type = _type;
1805 if (type == props::EXTENDED)
1806 type = _value.val->getType();
1809 result = _value.alias->setUnspecifiedValue(value);
1812 result = set_bool((compare_strings(value, "true")
1813 || atoi(value)) ? true : false);
1816 result = set_int(atoi(value));
1819 result = set_long(strtol(value, 0, 0));
1822 result = set_float(atof(value));
1825 result = set_double(strtod(value, 0));
1828 case props::UNSPECIFIED:
1829 result = set_string(value);
1831 #if !PROPS_STANDALONE
1833 result = static_cast<SGRawValue<SGVec3d>*>(_value.val)->setValue(parseString<SGVec3d>(value));
1836 result = static_cast<SGRawValue<SGVec4d>*>(_value.val)->setValue(parseString<SGVec4d>(value));
1844 if (getAttribute(TRACE_WRITE))
1849 //------------------------------------------------------------------------------
1850 #if !PROPS_STANDALONE
1851 bool SGPropertyNode::interpolate( const std::string& type,
1852 const SGPropertyNode& target,
1854 const std::string& easing )
1856 if( !_interpolation_mgr )
1858 SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available");
1860 // no interpolation possible -> set to target immediately
1861 setUnspecifiedValue( target.getStringValue() );
1865 return _interpolation_mgr->interpolate(this, type, target, duration, easing);
1868 //------------------------------------------------------------------------------
1869 bool SGPropertyNode::interpolate( const std::string& type,
1870 const simgear::PropertyList& values,
1871 const double_list& deltas,
1872 const std::string& easing )
1874 if( !_interpolation_mgr )
1876 SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available");
1878 // no interpolation possible -> set to last value immediately
1879 if( !values.empty() )
1880 setUnspecifiedValue(values.back()->getStringValue());
1884 return _interpolation_mgr->interpolate(this, type, values, deltas, easing);
1887 //------------------------------------------------------------------------------
1888 void SGPropertyNode::setInterpolationMgr(simgear::PropertyInterpolationMgr* mgr)
1890 _interpolation_mgr = mgr;
1893 //------------------------------------------------------------------------------
1894 simgear::PropertyInterpolationMgr* SGPropertyNode::getInterpolationMgr()
1896 return _interpolation_mgr;
1899 simgear::PropertyInterpolationMgr* SGPropertyNode::_interpolation_mgr = 0;
1902 //------------------------------------------------------------------------------
1903 std::ostream& SGPropertyNode::printOn(std::ostream& stream) const
1905 if (!getAttribute(READ))
1909 return _value.alias->printOn(stream);
1911 stream << (get_bool() ? "true" : "false");
1914 stream << get_int();
1917 stream << get_long();
1920 stream << get_float();
1923 stream << get_double();
1926 case props::UNSPECIFIED:
1927 stream << get_string();
1929 case props::EXTENDED:
1930 static_cast<SGRawExtended*>(_value.val)->printOn(stream);
1934 default: // avoid compiler warning
1941 bool SGPropertyNode::tie (const SGRawValue<const char *> &rawValue,
1944 if (_type == props::ALIAS || _tied)
1947 useDefault = useDefault && hasValue();
1948 std::string old_val;
1950 old_val = getStringValue();
1952 _type = props::STRING;
1954 _value.val = rawValue.clone();
1957 int save_attributes = getAttributes();
1958 setAttribute( WRITE, true );
1959 setStringValue(old_val.c_str());
1960 setAttributes( save_attributes );
1966 SGPropertyNode::untie ()
1973 bool val = getBoolValue();
1975 _type = props::BOOL;
1976 _local_val.bool_val = val;
1980 int val = getIntValue();
1983 _local_val.int_val = val;
1987 long val = getLongValue();
1989 _type = props::LONG;
1990 _local_val.long_val = val;
1993 case props::FLOAT: {
1994 float val = getFloatValue();
1996 _type = props::FLOAT;
1997 _local_val.float_val = val;
2000 case props::DOUBLE: {
2001 double val = getDoubleValue();
2003 _type = props::DOUBLE;
2004 _local_val.double_val = val;
2008 case props::UNSPECIFIED: {
2009 std::string val = getStringValue();
2011 _type = props::STRING;
2012 _local_val.string_val = copy_string(val.c_str());
2015 case props::EXTENDED: {
2016 SGRawExtended* val = static_cast<SGRawExtended*>(_value.val);
2017 _value.val = 0; // Prevent clearValue() from deleting
2019 _type = props::EXTENDED;
2020 _value.val = val->makeContainer();
2034 SGPropertyNode::getRootNode ()
2039 return _parent->getRootNode();
2042 const SGPropertyNode *
2043 SGPropertyNode::getRootNode () const
2048 return _parent->getRootNode();
2052 SGPropertyNode::getNode (const char * relative_path, bool create)
2054 #if PROPS_STANDALONE
2055 vector<PathComponent> components;
2056 parse_path(relative_path, components);
2057 return find_node(this, components, 0, create);
2060 using namespace boost;
2062 return find_node(this, make_iterator_range(relative_path, relative_path
2063 + strlen(relative_path)),
2069 SGPropertyNode::getNode (const char * relative_path, int index, bool create)
2071 #if PROPS_STANDALONE
2072 vector<PathComponent> components;
2073 parse_path(relative_path, components);
2074 if (components.size() > 0)
2075 components.back().index = index;
2076 return find_node(this, components, 0, create);
2079 using namespace boost;
2081 return find_node(this, make_iterator_range(relative_path, relative_path
2082 + strlen(relative_path)),
2087 const SGPropertyNode *
2088 SGPropertyNode::getNode (const char * relative_path) const
2090 return ((SGPropertyNode *)this)->getNode(relative_path, false);
2093 const SGPropertyNode *
2094 SGPropertyNode::getNode (const char * relative_path, int index) const
2096 return ((SGPropertyNode *)this)->getNode(relative_path, index, false);
2099 ////////////////////////////////////////////////////////////////////////
2100 // Convenience methods using relative paths.
2101 ////////////////////////////////////////////////////////////////////////
2105 * Test whether another node has a value attached.
2108 SGPropertyNode::hasValue (const char * relative_path) const
2110 const SGPropertyNode * node = getNode(relative_path);
2111 return (node == 0 ? false : node->hasValue());
2116 * Get the value type for another node.
2119 SGPropertyNode::getType (const char * relative_path) const
2121 const SGPropertyNode * node = getNode(relative_path);
2122 return (node == 0 ? props::UNSPECIFIED : node->getType());
2127 * Get a bool value for another node.
2130 SGPropertyNode::getBoolValue (const char * relative_path,
2131 bool defaultValue) const
2133 const SGPropertyNode * node = getNode(relative_path);
2134 return (node == 0 ? defaultValue : node->getBoolValue());
2139 * Get an int value for another node.
2142 SGPropertyNode::getIntValue (const char * relative_path,
2143 int defaultValue) const
2145 const SGPropertyNode * node = getNode(relative_path);
2146 return (node == 0 ? defaultValue : node->getIntValue());
2151 * Get a long value for another node.
2154 SGPropertyNode::getLongValue (const char * relative_path,
2155 long defaultValue) const
2157 const SGPropertyNode * node = getNode(relative_path);
2158 return (node == 0 ? defaultValue : node->getLongValue());
2163 * Get a float value for another node.
2166 SGPropertyNode::getFloatValue (const char * relative_path,
2167 float defaultValue) const
2169 const SGPropertyNode * node = getNode(relative_path);
2170 return (node == 0 ? defaultValue : node->getFloatValue());
2175 * Get a double value for another node.
2178 SGPropertyNode::getDoubleValue (const char * relative_path,
2179 double defaultValue) const
2181 const SGPropertyNode * node = getNode(relative_path);
2182 return (node == 0 ? defaultValue : node->getDoubleValue());
2187 * Get a string value for another node.
2190 SGPropertyNode::getStringValue (const char * relative_path,
2191 const char * defaultValue) const
2193 const SGPropertyNode * node = getNode(relative_path);
2194 return (node == 0 ? defaultValue : node->getStringValue());
2199 * Set a bool value for another node.
2202 SGPropertyNode::setBoolValue (const char * relative_path, bool value)
2204 return getNode(relative_path, true)->setBoolValue(value);
2209 * Set an int value for another node.
2212 SGPropertyNode::setIntValue (const char * relative_path, int value)
2214 return getNode(relative_path, true)->setIntValue(value);
2219 * Set a long value for another node.
2222 SGPropertyNode::setLongValue (const char * relative_path, long value)
2224 return getNode(relative_path, true)->setLongValue(value);
2229 * Set a float value for another node.
2232 SGPropertyNode::setFloatValue (const char * relative_path, float value)
2234 return getNode(relative_path, true)->setFloatValue(value);
2239 * Set a double value for another node.
2242 SGPropertyNode::setDoubleValue (const char * relative_path, double value)
2244 return getNode(relative_path, true)->setDoubleValue(value);
2249 * Set a string value for another node.
2252 SGPropertyNode::setStringValue (const char * relative_path, const char * value)
2254 return getNode(relative_path, true)->setStringValue(value);
2259 * Set an unknown value for another node.
2262 SGPropertyNode::setUnspecifiedValue (const char * relative_path,
2265 return getNode(relative_path, true)->setUnspecifiedValue(value);
2270 * Test whether another node is tied.
2273 SGPropertyNode::isTied (const char * relative_path) const
2275 const SGPropertyNode * node = getNode(relative_path);
2276 return (node == 0 ? false : node->isTied());
2281 * Tie a node reached by a relative path, creating it if necessary.
2284 SGPropertyNode::tie (const char * relative_path,
2285 const SGRawValue<bool> &rawValue,
2288 return getNode(relative_path, true)->tie(rawValue, useDefault);
2293 * Tie a node reached by a relative path, creating it if necessary.
2296 SGPropertyNode::tie (const char * relative_path,
2297 const SGRawValue<int> &rawValue,
2300 return getNode(relative_path, true)->tie(rawValue, useDefault);
2305 * Tie a node reached by a relative path, creating it if necessary.
2308 SGPropertyNode::tie (const char * relative_path,
2309 const SGRawValue<long> &rawValue,
2312 return getNode(relative_path, true)->tie(rawValue, useDefault);
2317 * Tie a node reached by a relative path, creating it if necessary.
2320 SGPropertyNode::tie (const char * relative_path,
2321 const SGRawValue<float> &rawValue,
2324 return getNode(relative_path, true)->tie(rawValue, useDefault);
2329 * Tie a node reached by a relative path, creating it if necessary.
2332 SGPropertyNode::tie (const char * relative_path,
2333 const SGRawValue<double> &rawValue,
2336 return getNode(relative_path, true)->tie(rawValue, useDefault);
2341 * Tie a node reached by a relative path, creating it if necessary.
2344 SGPropertyNode::tie (const char * relative_path,
2345 const SGRawValue<const char *> &rawValue,
2348 return getNode(relative_path, true)->tie(rawValue, useDefault);
2353 * Attempt to untie another node reached by a relative path.
2356 SGPropertyNode::untie (const char * relative_path)
2358 SGPropertyNode * node = getNode(relative_path);
2359 return (node == 0 ? false : node->untie());
2363 SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
2366 if (_listeners == 0)
2367 _listeners = new vector<SGPropertyChangeListener*>;
2368 _listeners->push_back(listener);
2369 listener->register_property(this);
2371 listener->valueChanged(this);
2375 SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
2377 if (_listeners == 0)
2379 vector<SGPropertyChangeListener*>::iterator it =
2380 find(_listeners->begin(), _listeners->end(), listener);
2381 if (it != _listeners->end()) {
2382 _listeners->erase(it);
2383 listener->unregister_property(this);
2384 if (_listeners->empty()) {
2385 vector<SGPropertyChangeListener*>* tmp = _listeners;
2393 SGPropertyNode::fireValueChanged ()
2395 fireValueChanged(this);
2399 SGPropertyNode::fireChildAdded (SGPropertyNode * child)
2401 fireChildAdded(this, child);
2405 SGPropertyNode::fireCreatedRecursive(bool fire_self)
2409 _parent->fireChildAdded(this);
2411 if( _children.empty() && getType() != simgear::props::NONE )
2412 return fireValueChanged();
2415 for(size_t i = 0; i < _children.size(); ++i)
2416 _children[i]->fireCreatedRecursive(true);
2420 SGPropertyNode::fireChildRemoved (SGPropertyNode * child)
2422 fireChildRemoved(this, child);
2426 SGPropertyNode::fireChildrenRemovedRecursive()
2428 for(size_t i = 0; i < _children.size(); ++i)
2430 SGPropertyNode* child = _children[i];
2431 fireChildRemoved(this, child);
2432 child->fireChildrenRemovedRecursive();
2437 SGPropertyNode::fireValueChanged (SGPropertyNode * node)
2439 if (_listeners != 0) {
2440 for (unsigned int i = 0; i < _listeners->size(); i++) {
2441 (*_listeners)[i]->valueChanged(node);
2445 _parent->fireValueChanged(node);
2449 SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
2450 SGPropertyNode * child)
2452 if (_listeners != 0) {
2453 for (unsigned int i = 0; i < _listeners->size(); i++) {
2454 (*_listeners)[i]->childAdded(parent, child);
2458 _parent->fireChildAdded(parent, child);
2462 SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
2463 SGPropertyNode * child)
2465 if (_listeners != 0) {
2466 for (unsigned int i = 0; i < _listeners->size(); i++) {
2467 (*_listeners)[i]->childRemoved(parent, child);
2471 _parent->fireChildRemoved(parent, child);
2474 //------------------------------------------------------------------------------
2476 SGPropertyNode::eraseChild(simgear::PropertyList::iterator child)
2478 SGPropertyNode_ptr node = *child;
2479 node->setAttribute(REMOVED, true);
2481 fireChildRemoved(node);
2483 _children.erase(child);
2487 ////////////////////////////////////////////////////////////////////////
2488 // Implementation of SGPropertyChangeListener.
2489 ////////////////////////////////////////////////////////////////////////
2491 SGPropertyChangeListener::~SGPropertyChangeListener ()
2493 for (int i = static_cast<int>(_properties.size() - 1); i >= 0; i--)
2494 _properties[i]->removeChangeListener(this);
2498 SGPropertyChangeListener::valueChanged (SGPropertyNode * node)
2504 SGPropertyChangeListener::childAdded (SGPropertyNode * node,
2505 SGPropertyNode * child)
2511 SGPropertyChangeListener::childRemoved (SGPropertyNode * parent,
2512 SGPropertyNode * child)
2518 SGPropertyChangeListener::register_property (SGPropertyNode * node)
2520 _properties.push_back(node);
2524 SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
2526 vector<SGPropertyNode *>::iterator it =
2527 find(_properties.begin(), _properties.end(), node);
2528 if (it != _properties.end())
2529 _properties.erase(it);
2532 #if !PROPS_STANDALONE
2534 std::ostream& SGRawBase<SGVec3d>::printOn(std::ostream& stream) const
2537 = static_cast<const SGRawValue<SGVec3d>*>(this)->getValue();
2538 for (int i = 0; i < 3; ++i) {
2549 std::istream& readFrom<SGVec3d>(std::istream& stream, SGVec3d& result)
2551 for (int i = 0; i < 3; ++i) {
2552 stream >> result[i];
2558 std::ostream& SGRawBase<SGVec4d>::printOn(std::ostream& stream) const
2561 = static_cast<const SGRawValue<SGVec4d>*>(this)->getValue();
2562 for (int i = 0; i < 4; ++i) {
2573 #if !PROPS_STANDALONE
2575 std::istream& readFrom<SGVec4d>(std::istream& stream, SGVec4d& result)
2577 for (int i = 0; i < 4; ++i) {
2578 stream >> result[i];
2586 bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs)
2588 props::Type ltype = lhs.getType();
2589 props::Type rtype = rhs.getType();
2596 return false; // XXX Should we look in aliases?
2598 return lhs.getValue<bool>() == rhs.getValue<bool>();
2600 return lhs.getValue<int>() == rhs.getValue<int>();
2602 return lhs.getValue<long>() == rhs.getValue<long>();
2604 return lhs.getValue<float>() == rhs.getValue<float>();
2606 return lhs.getValue<double>() == rhs.getValue<double>();
2608 case props::UNSPECIFIED:
2609 return !strcmp(lhs.getStringValue(), rhs.getStringValue());
2610 #if !PROPS_STANDALONE
2612 return lhs.getValue<SGVec3d>() == rhs.getValue<SGVec3d>();
2614 return lhs.getValue<SGVec4d>() == rhs.getValue<SGVec4d>();
2623 bool SGPropertyNode::compare(const SGPropertyNode& lhs,
2624 const SGPropertyNode& rhs)
2628 int lhsChildren = lhs.nChildren();
2629 int rhsChildren = rhs.nChildren();
2630 if (lhsChildren != rhsChildren)
2632 if (lhsChildren == 0)
2633 return compareNodeValue(lhs, rhs);
2634 for (size_t i = 0; i < lhs._children.size(); ++i) {
2635 const SGPropertyNode* lchild = lhs._children[i];
2636 const SGPropertyNode* rchild = rhs._children[i];
2637 // I'm guessing that the nodes will usually be in the same
2639 if (lchild->getIndex() != rchild->getIndex()
2640 || lchild->getNameString() != rchild->getNameString()) {
2642 for (PropertyList::const_iterator itr = rhs._children.begin(),
2643 end = rhs._children.end();
2646 if (lchild->getIndex() == (*itr)->getIndex()
2647 && lchild->getNameString() == (*itr)->getNameString()) {
2654 if (!compare(*lchild, *rchild))
2660 struct PropertyPlaceLess {
2661 typedef bool result_type;
2662 bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
2664 int comp = lhs->getNameString().compare(rhs->getNameString());
2666 return lhs->getIndex() < rhs->getIndex();
2672 #if !PROPS_STANDALONE
2673 size_t hash_value(const SGPropertyNode& node)
2675 using namespace boost;
2676 if (node.nChildren() == 0) {
2677 switch (node.getType()) {
2682 return hash_value(node.getValue<bool>());
2684 return hash_value(node.getValue<int>());
2686 return hash_value(node.getValue<long>());
2688 return hash_value(node.getValue<float>());
2690 return hash_value(node.getValue<double>());
2692 case props::UNSPECIFIED:
2694 const char *val = node.getStringValue();
2695 return hash_range(val, val + strlen(val));
2699 const SGVec3d val = node.getValue<SGVec3d>();
2700 return hash_range(&val[0], &val[3]);
2704 const SGVec4d val = node.getValue<SGVec4d>();
2705 return hash_range(&val[0], &val[4]);
2707 case props::ALIAS: // XXX Should we look in aliases?
2713 PropertyList children(node._children.begin(), node._children.end());
2714 sort(children.begin(), children.end(), PropertyPlaceLess());
2715 for (PropertyList::const_iterator itr = children.begin(),
2716 end = children.end();
2719 hash_combine(seed, (*itr)->_name);
2720 hash_combine(seed, (*itr)->_index);
2721 hash_combine(seed, hash_value(**itr));