Files
simgear/simgear/props/props.cxx
T
Tim Moore ce67657e0a eliminate property node path cache
The property path cache was making very little difference in practice
and made the eventual goal of having the property tree be thread safe
for readers more difficult to attain.
2010-11-17 11:25:07 +01:00

2326 lines
52 KiB
C++

// props.cxx - implementation of a property list.
// Started Fall 2000 by David Megginson, david@megginson.com
// This code is released into the Public Domain.
//
// See props.html for documentation [replace with URL when available].
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include "props.hxx"
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <iterator>
#include <stdio.h>
#include <string.h>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/bind.hpp>
#include <boost/functional/hash.hpp>
#include <boost/range.hpp>
#include <simgear/math/SGMath.hxx>
#if PROPS_STANDALONE
#include <iostream>
#else
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#if ( _MSC_VER == 1200 )
// MSVC 6 is buggy, and needs something strange here
using std::vector<SGPropertyNode_ptr>;
using std::vector<SGPropertyChangeListener *>;
using std::vector<SGPropertyNode *>;
#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;
////////////////////////////////////////////////////////////////////////
// Local classes.
////////////////////////////////////////////////////////////////////////
/**
* Comparator class for sorting by index.
*/
class CompareIndices
{
public:
int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
return (n1->getIndex() < n2->getIndex());
}
};
////////////////////////////////////////////////////////////////////////
// Convenience macros for value access.
////////////////////////////////////////////////////////////////////////
#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
#define TEST_WRITE if (!getAttribute(WRITE)) return false
////////////////////////////////////////////////////////////////////////
// Local path normalization code.
////////////////////////////////////////////////////////////////////////
/**
* Parse the name for a path component.
*
* Name: [_a-zA-Z][-._a-zA-Z0-9]*
*/
template<typename Range>
inline Range
parse_name (const SGPropertyNode *node, const Range &path)
{
typename Range::iterator i = path.begin();
typename Range::iterator max = path.end();
if (*i == '.') {
i++;
if (i != path.end() && *i == '.') {
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(*i) || isdigit(*i) || *i == '_' ||
*i == '-' || *i == '.') {
// name += path[i];
} else if (*i == '[' || *i == '/') {
break;
} else {
string err = "'";
err.push_back(*i);
err.append("' found in propertyname after '"+node->getNameString()+"'");
err.append("\nname may contain only ._- and alphanumeric characters");
throw err;
}
i++;
}
}
else {
if (path.begin() == i) {
string err = "'";
err.push_back(*i);
err.append("' found in propertyname after '"+node->getNameString()+"'");
err.append("\nname must begin with alpha or '_'");
throw err;
}
}
return Range(path.begin(), i);
}
// Validate the name of a single node
inline bool validateName(const string& name)
{
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.
////////////////////////////////////////////////////////////////////////
static char *
copy_string (const char * 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;
}
static bool
compare_strings (const char * s1, const char * s2)
{
return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
}
/**
* Locate a child node by name and index.
*/
template<typename Itr>
static int
find_child (Itr begin, Itr end, int index, const PropertyList& nodes)
{
int nNodes = nodes.size();
boost::iterator_range<Itr> name(begin, end);
for (int i = 0; i < nNodes; i++) {
SGPropertyNode * node = nodes[i];
// 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 the child node with the highest index of the same name
*/
static int
find_last_child (const char * name, const PropertyList& nodes)
{
int nNodes = nodes.size();
int index = 0;
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;
}
template<typename Itr>
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<typename Itr>
SGPropertyNode *
SGPropertyNode::getChildImpl (Itr begin, Itr end, int index, bool create)
{
SGPropertyNode* node = getExistingChild(begin, end, index, 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<typename SplitItr>
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;
}
// 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(current, 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 '..'");
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<typename Range>
SGPropertyNode*
find_node (SGPropertyNode * current,
const Range& path,
bool create,
int last_index = -1)
{
using namespace boost;
typedef split_iterator<typename range_result_iterator<Range>::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);
}
////////////////////////////////////////////////////////////////////////
// Private methods from SGPropertyNode (may be inlined for speed).
////////////////////////////////////////////////////////////////////////
inline bool
SGPropertyNode::get_bool () const
{
if (_tied)
return static_cast<SGRawValue<bool>*>(_value.val)->getValue();
else
return _local_val.bool_val;
}
inline int
SGPropertyNode::get_int () const
{
if (_tied)
return (static_cast<SGRawValue<int>*>(_value.val))->getValue();
else
return _local_val.int_val;
}
inline long
SGPropertyNode::get_long () const
{
if (_tied)
return static_cast<SGRawValue<long>*>(_value.val)->getValue();
else
return _local_val.long_val;
}
inline float
SGPropertyNode::get_float () const
{
if (_tied)
return static_cast<SGRawValue<float>*>(_value.val)->getValue();
else
return _local_val.float_val;
}
inline double
SGPropertyNode::get_double () const
{
if (_tied)
return static_cast<SGRawValue<double>*>(_value.val)->getValue();
else
return _local_val.double_val;
}
inline const char *
SGPropertyNode::get_string () const
{
if (_tied)
return static_cast<SGRawValue<const char*>*>(_value.val)->getValue();
else
return _local_val.string_val;
}
inline bool
SGPropertyNode::set_bool (bool val)
{
if (_tied) {
if (static_cast<SGRawValue<bool>*>(_value.val)->setValue(val)) {
fireValueChanged();
return true;
} else {
return false;
}
} else {
_local_val.bool_val = val;
fireValueChanged();
return true;
}
}
inline bool
SGPropertyNode::set_int (int val)
{
if (_tied) {
if (static_cast<SGRawValue<int>*>(_value.val)->setValue(val)) {
fireValueChanged();
return true;
} else {
return false;
}
} else {
_local_val.int_val = val;
fireValueChanged();
return true;
}
}
inline bool
SGPropertyNode::set_long (long val)
{
if (_tied) {
if (static_cast<SGRawValue<long>*>(_value.val)->setValue(val)) {
fireValueChanged();
return true;
} else {
return false;
}
} else {
_local_val.long_val = val;
fireValueChanged();
return true;
}
}
inline bool
SGPropertyNode::set_float (float val)
{
if (_tied) {
if (static_cast<SGRawValue<float>*>(_value.val)->setValue(val)) {
fireValueChanged();
return true;
} else {
return false;
}
} else {
_local_val.float_val = val;
fireValueChanged();
return true;
}
}
inline bool
SGPropertyNode::set_double (double val)
{
if (_tied) {
if (static_cast<SGRawValue<double>*>(_value.val)->setValue(val)) {
fireValueChanged();
return true;
} else {
return false;
}
} else {
_local_val.double_val = val;
fireValueChanged();
return true;
}
}
inline bool
SGPropertyNode::set_string (const char * val)
{
if (_tied) {
if (static_cast<SGRawValue<const char*>*>(_value.val)->setValue(val)) {
fireValueChanged();
return true;
} else {
return false;
}
} else {
delete [] _local_val.string_val;
_local_val.string_val = copy_string(val);
fireValueChanged();
return true;
}
}
void
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<bool>::DefaultValue();
break;
case props::INT:
_local_val.int_val = SGRawValue<int>::DefaultValue();
break;
case props::LONG:
_local_val.long_val = SGRawValue<long>::DefaultValue();
break;
case props::FLOAT:
_local_val.float_val = SGRawValue<float>::DefaultValue();
break;
case props::DOUBLE:
_local_val.double_val = SGRawValue<double>::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;
}
_tied = false;
_type = props::NONE;
}
/**
* Get the value as a string.
*/
const char *
SGPropertyNode::make_string () const
{
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<SGRawExtended*>(_value.val)->printOn(sstr);
}
break;
default:
return "";
}
_buffer = sstr.str();
return _buffer.c_str();
}
/**
* Trace a write access for a property.
*/
void
SGPropertyNode::trace_write () const
{
#if PROPS_STANDALONE
cerr << "TRACE: Write node " << getPath () << ", value \""
<< make_string() << '"' << endl;
#else
SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << getPath()
<< ", value \"" << make_string() << '"');
#endif
}
/**
* Trace a read access for a property.
*/
void
SGPropertyNode::trace_read () const
{
#if PROPS_STANDALONE
cerr << "TRACE: Write node " << getPath () << ", value \""
<< make_string() << '"' << endl;
#else
SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << getPath()
<< ", value \"" << make_string() << '"');
#endif
}
////////////////////////////////////////////////////////////////////////
// Public methods from SGPropertyNode.
////////////////////////////////////////////////////////////////////////
/**
* Last used attribute
* Update as needed when enum Attribute is changed
*/
const int SGPropertyNode::LAST_USED_ATTRIBUTE = USERARCHIVE;
/**
* Default constructor: always creates a root node.
*/
SGPropertyNode::SGPropertyNode ()
: _index(0),
_parent(0),
_type(props::NONE),
_tied(false),
_attr(READ|WRITE),
_listeners(0)
{
_local_val.string_val = 0;
_value.val = 0;
}
/**
* Copy constructor.
*/
SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
: _index(node._index),
_name(node._name),
_parent(0), // don't copy the parent
_type(node._type),
_tied(node._tied),
_attr(node._attr),
_listeners(0) // CHECK!!
{
_local_val.string_val = 0;
_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 props::INT:
set_int(node.get_int());
break;
case props::LONG:
set_long(node.get_long());
break;
case props::FLOAT:
set_float(node.get_float());
break;
case props::DOUBLE:
set_double(node.get_double());
break;
case props::STRING:
case props::UNSPECIFIED:
set_string(node.get_string());
break;
default:
break;
}
}
/**
* Convenience constructor.
*/
template<typename Itr>
SGPropertyNode::SGPropertyNode (Itr begin, Itr end,
int index,
SGPropertyNode * parent)
: _index(index),
_name(begin, end),
_parent(parent),
_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 + '\'';
}
SGPropertyNode::SGPropertyNode (const string& name,
int index,
SGPropertyNode * parent)
: _index(index),
_name(name),
_parent(parent),
_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 ()
{
// 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;
clearValue();
if (_listeners) {
vector<SGPropertyChangeListener*>::iterator it;
for (it = _listeners->begin(); it != _listeners->end(); ++it)
(*it)->unregister_property(this);
delete _listeners;
}
}
/**
* Alias to another node.
*/
bool
SGPropertyNode::alias (SGPropertyNode * target)
{
if (target == 0 || _type == props::ALIAS || _tied)
return false;
clearValue();
get(target);
_value.alias = target;
_type = props::ALIAS;
return true;
}
/**
* Alias to another node by path.
*/
bool
SGPropertyNode::alias (const char * path)
{
return alias(getNode(path, true));
}
/**
* Remove an alias.
*/
bool
SGPropertyNode::unalias ()
{
if (_type != props::ALIAS)
return false;
clearValue();
return true;
}
/**
* Get the target of an alias.
*/
SGPropertyNode *
SGPropertyNode::getAliasTarget ()
{
return (_type == props::ALIAS ? _value.alias : 0);
}
const SGPropertyNode *
SGPropertyNode::getAliasTarget () const
{
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;
}
/**
* Get a non-const child by index.
*/
SGPropertyNode *
SGPropertyNode::getChild (int position)
{
if (position >= 0 && position < nChildren())
return _children[position];
else
return 0;
}
/**
* Get a const child by index.
*/
const SGPropertyNode *
SGPropertyNode::getChild (int position) const
{
if (position >= 0 && position < nChildren())
return _children[position];
else
return 0;
}
/**
* Get a non-const child by name and index, creating if necessary.
*/
SGPropertyNode *
SGPropertyNode::getChild (const char * name, int index, bool create)
{
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;
}
}
/**
* Get a const child by name and index.
*/
const SGPropertyNode *
SGPropertyNode::getChild (const char * name, int index) const
{
int pos = find_child(name, name + strlen(name), index, _children);
if (pos >= 0)
return _children[pos];
else
return 0;
}
/**
* Get all children with the same name (but different indices).
*/
PropertyList
SGPropertyNode::getChildren (const char * name) const
{
PropertyList children;
int max = _children.size();
for (int i = 0; i < max; i++)
if (compare_strings(_children[i]->getName(), name))
children.push_back(_children[i]);
sort(children.begin(), children.end(), CompareIndices());
return children;
}
/**
* 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->setAttribute(REMOVED, true);
node->clearValue();
fireChildRemoved(node);
return node;
}
/**
* Remove a child node
*/
SGPropertyNode_ptr
SGPropertyNode::removeChild (const char * name, int index, bool keep)
{
SGPropertyNode_ptr ret;
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;
}
string
SGPropertyNode::getDisplayName (bool simplify) const
{
string display_name = _name;
if (_index != 0 || !simplify) {
stringstream sstr;
sstr << '[' << _index << ']';
display_name += sstr.str();
}
return display_name;
}
string
SGPropertyNode::getPath (bool simplify) const
{
typedef std::vector<SGConstPropertyNode_ptr> 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 result;
}
props::Type
SGPropertyNode::getType () const
{
if (_type == props::ALIAS)
return _value.alias->getType();
else if (_type == props::EXTENDED)
return _value.val->getType();
else
return _type;
}
bool
SGPropertyNode::getBoolValue () const
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::BOOL)
return get_bool();
if (getAttribute(TRACE_READ))
trace_read();
if (!getAttribute(READ))
return SGRawValue<bool>::DefaultValue();
switch (_type) {
case props::ALIAS:
return _value.alias->getBoolValue();
case props::BOOL:
return get_bool();
case props::INT:
return get_int() == 0 ? false : true;
case props::LONG:
return get_long() == 0L ? false : true;
case props::FLOAT:
return get_float() == 0.0 ? false : true;
case props::DOUBLE:
return get_double() == 0.0L ? false : true;
case props::STRING:
case props::UNSPECIFIED:
return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
case props::NONE:
default:
return SGRawValue<bool>::DefaultValue();
}
}
int
SGPropertyNode::getIntValue () const
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::INT)
return get_int();
if (getAttribute(TRACE_READ))
trace_read();
if (!getAttribute(READ))
return SGRawValue<int>::DefaultValue();
switch (_type) {
case props::ALIAS:
return _value.alias->getIntValue();
case props::BOOL:
return int(get_bool());
case props::INT:
return get_int();
case props::LONG:
return int(get_long());
case props::FLOAT:
return int(get_float());
case props::DOUBLE:
return int(get_double());
case props::STRING:
case props::UNSPECIFIED:
return atoi(get_string());
case props::NONE:
default:
return SGRawValue<int>::DefaultValue();
}
}
long
SGPropertyNode::getLongValue () const
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::LONG)
return get_long();
if (getAttribute(TRACE_READ))
trace_read();
if (!getAttribute(READ))
return SGRawValue<long>::DefaultValue();
switch (_type) {
case props::ALIAS:
return _value.alias->getLongValue();
case props::BOOL:
return long(get_bool());
case props::INT:
return long(get_int());
case props::LONG:
return get_long();
case props::FLOAT:
return long(get_float());
case props::DOUBLE:
return long(get_double());
case props::STRING:
case props::UNSPECIFIED:
return strtol(get_string(), 0, 0);
case props::NONE:
default:
return SGRawValue<long>::DefaultValue();
}
}
float
SGPropertyNode::getFloatValue () const
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::FLOAT)
return get_float();
if (getAttribute(TRACE_READ))
trace_read();
if (!getAttribute(READ))
return SGRawValue<float>::DefaultValue();
switch (_type) {
case props::ALIAS:
return _value.alias->getFloatValue();
case props::BOOL:
return float(get_bool());
case props::INT:
return float(get_int());
case props::LONG:
return float(get_long());
case props::FLOAT:
return get_float();
case props::DOUBLE:
return float(get_double());
case props::STRING:
case props::UNSPECIFIED:
return atof(get_string());
case props::NONE:
default:
return SGRawValue<float>::DefaultValue();
}
}
double
SGPropertyNode::getDoubleValue () const
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::DOUBLE)
return get_double();
if (getAttribute(TRACE_READ))
trace_read();
if (!getAttribute(READ))
return SGRawValue<double>::DefaultValue();
switch (_type) {
case props::ALIAS:
return _value.alias->getDoubleValue();
case props::BOOL:
return double(get_bool());
case props::INT:
return double(get_int());
case props::LONG:
return double(get_long());
case props::FLOAT:
return double(get_float());
case props::DOUBLE:
return get_double();
case props::STRING:
case props::UNSPECIFIED:
return strtod(get_string(), 0);
case props::NONE:
default:
return SGRawValue<double>::DefaultValue();
}
}
const char *
SGPropertyNode::getStringValue () const
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::STRING)
return get_string();
if (getAttribute(TRACE_READ))
trace_read();
if (!getAttribute(READ))
return SGRawValue<const char *>::DefaultValue();
return make_string();
}
bool
SGPropertyNode::setBoolValue (bool value)
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::BOOL)
return set_bool(value);
bool result = false;
TEST_WRITE;
if (_type == props::NONE || _type == props::UNSPECIFIED) {
clearValue();
_tied = false;
_type = props::BOOL;
}
switch (_type) {
case props::ALIAS:
result = _value.alias->setBoolValue(value);
break;
case props::BOOL:
result = set_bool(value);
break;
case props::INT:
result = set_int(int(value));
break;
case props::LONG:
result = set_long(long(value));
break;
case props::FLOAT:
result = set_float(float(value));
break;
case props::DOUBLE:
result = set_double(double(value));
break;
case props::STRING:
case props::UNSPECIFIED:
result = set_string(value ? "true" : "false");
break;
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
bool
SGPropertyNode::setIntValue (int value)
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::INT)
return set_int(value);
bool result = false;
TEST_WRITE;
if (_type == props::NONE || _type == props::UNSPECIFIED) {
clearValue();
_type = props::INT;
_local_val.int_val = 0;
}
switch (_type) {
case props::ALIAS:
result = _value.alias->setIntValue(value);
break;
case props::BOOL:
result = set_bool(value == 0 ? false : true);
break;
case props::INT:
result = set_int(value);
break;
case props::LONG:
result = set_long(long(value));
break;
case props::FLOAT:
result = set_float(float(value));
break;
case props::DOUBLE:
result = set_double(double(value));
break;
case props::STRING:
case props::UNSPECIFIED: {
char buf[128];
sprintf(buf, "%d", value);
result = set_string(buf);
break;
}
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
bool
SGPropertyNode::setLongValue (long value)
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::LONG)
return set_long(value);
bool result = false;
TEST_WRITE;
if (_type == props::NONE || _type == props::UNSPECIFIED) {
clearValue();
_type = props::LONG;
_local_val.long_val = 0L;
}
switch (_type) {
case props::ALIAS:
result = _value.alias->setLongValue(value);
break;
case props::BOOL:
result = set_bool(value == 0L ? false : true);
break;
case props::INT:
result = set_int(int(value));
break;
case props::LONG:
result = set_long(value);
break;
case props::FLOAT:
result = set_float(float(value));
break;
case props::DOUBLE:
result = set_double(double(value));
break;
case props::STRING:
case props::UNSPECIFIED: {
char buf[128];
sprintf(buf, "%ld", value);
result = set_string(buf);
break;
}
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
bool
SGPropertyNode::setFloatValue (float value)
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::FLOAT)
return set_float(value);
bool result = false;
TEST_WRITE;
if (_type == props::NONE || _type == props::UNSPECIFIED) {
clearValue();
_type = props::FLOAT;
_local_val.float_val = 0;
}
switch (_type) {
case props::ALIAS:
result = _value.alias->setFloatValue(value);
break;
case props::BOOL:
result = set_bool(value == 0.0 ? false : true);
break;
case props::INT:
result = set_int(int(value));
break;
case props::LONG:
result = set_long(long(value));
break;
case props::FLOAT:
result = set_float(value);
break;
case props::DOUBLE:
result = set_double(double(value));
break;
case props::STRING:
case props::UNSPECIFIED: {
char buf[128];
sprintf(buf, "%f", value);
result = set_string(buf);
break;
}
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
bool
SGPropertyNode::setDoubleValue (double value)
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::DOUBLE)
return set_double(value);
bool result = false;
TEST_WRITE;
if (_type == props::NONE || _type == props::UNSPECIFIED) {
clearValue();
_local_val.double_val = value;
_type = props::DOUBLE;
}
switch (_type) {
case props::ALIAS:
result = _value.alias->setDoubleValue(value);
break;
case props::BOOL:
result = set_bool(value == 0.0L ? false : true);
break;
case props::INT:
result = set_int(int(value));
break;
case props::LONG:
result = set_long(long(value));
break;
case props::FLOAT:
result = set_float(float(value));
break;
case props::DOUBLE:
result = set_double(value);
break;
case props::STRING:
case props::UNSPECIFIED: {
char buf[128];
sprintf(buf, "%f", value);
result = set_string(buf);
break;
}
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
bool
SGPropertyNode::setStringValue (const char * value)
{
// Shortcut for common case
if (_attr == (READ|WRITE) && _type == props::STRING)
return set_string(value);
bool result = false;
TEST_WRITE;
if (_type == props::NONE || _type == props::UNSPECIFIED) {
clearValue();
_type = props::STRING;
}
switch (_type) {
case props::ALIAS:
result = _value.alias->setStringValue(value);
break;
case props::BOOL:
result = set_bool((compare_strings(value, "true")
|| atoi(value)) ? true : false);
break;
case props::INT:
result = set_int(atoi(value));
break;
case props::LONG:
result = set_long(strtol(value, 0, 0));
break;
case props::FLOAT:
result = set_float(atof(value));
break;
case props::DOUBLE:
result = set_double(strtod(value, 0));
break;
case props::STRING:
case props::UNSPECIFIED:
result = set_string(value);
break;
case props::EXTENDED:
{
stringstream sstr(value);
static_cast<SGRawExtended*>(_value.val)->readFrom(sstr);
}
break;
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
bool
SGPropertyNode::setUnspecifiedValue (const char * value)
{
bool result = false;
TEST_WRITE;
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 props::BOOL:
result = set_bool((compare_strings(value, "true")
|| atoi(value)) ? true : false);
break;
case props::INT:
result = set_int(atoi(value));
break;
case props::LONG:
result = set_long(strtol(value, 0, 0));
break;
case props::FLOAT:
result = set_float(atof(value));
break;
case props::DOUBLE:
result = set_double(strtod(value, 0));
break;
case props::STRING:
case props::UNSPECIFIED:
result = set_string(value);
break;
case props::VEC3D:
result = static_cast<SGRawValue<SGVec3d>*>(_value.val)->setValue(parseString<SGVec3d>(value));
break;
case props::VEC4D:
result = static_cast<SGRawValue<SGVec4d>*>(_value.val)->setValue(parseString<SGVec4d>(value));
break;
case props::NONE:
default:
break;
}
if (getAttribute(TRACE_WRITE))
trace_write();
return result;
}
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<SGRawExtended*>(_value.val)->printOn(stream);
break;
case props::NONE:
break;
default: // avoid compiler warning
break;
}
return stream;
}
template<>
bool SGPropertyNode::tie (const SGRawValue<const char *> &rawValue,
bool useDefault)
{
if (_type == props::ALIAS || _tied)
return false;
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());
return true;
}
bool
SGPropertyNode::untie ()
{
if (!_tied)
return false;
switch (_type) {
case props::BOOL: {
bool val = getBoolValue();
clearValue();
_type = props::BOOL;
_local_val.bool_val = val;
break;
}
case props::INT: {
int val = getIntValue();
clearValue();
_type = props::INT;
_local_val.int_val = val;
break;
}
case props::LONG: {
long val = getLongValue();
clearValue();
_type = props::LONG;
_local_val.long_val = val;
break;
}
case props::FLOAT: {
float val = getFloatValue();
clearValue();
_type = props::FLOAT;
_local_val.float_val = val;
break;
}
case props::DOUBLE: {
double val = getDoubleValue();
clearValue();
_type = props::DOUBLE;
_local_val.double_val = val;
break;
}
case props::STRING:
case props::UNSPECIFIED: {
string val = getStringValue();
clearValue();
_type = props::STRING;
_local_val.string_val = copy_string(val.c_str());
break;
}
case props::EXTENDED: {
SGRawExtended* val = static_cast<SGRawExtended*>(_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;
}
_tied = false;
return true;
}
SGPropertyNode *
SGPropertyNode::getRootNode ()
{
if (_parent == 0)
return this;
else
return _parent->getRootNode();
}
const SGPropertyNode *
SGPropertyNode::getRootNode () const
{
if (_parent == 0)
return this;
else
return _parent->getRootNode();
}
SGPropertyNode *
SGPropertyNode::getNode (const char * relative_path, bool create)
{
using namespace boost;
return find_node(this, make_iterator_range(relative_path, relative_path
+ strlen(relative_path)),
create);
}
SGPropertyNode *
SGPropertyNode::getNode (const char * relative_path, int index, bool create)
{
using namespace boost;
return find_node(this, make_iterator_range(relative_path, relative_path
+ strlen(relative_path)),
create, index);
}
const SGPropertyNode *
SGPropertyNode::getNode (const char * relative_path) const
{
return ((SGPropertyNode *)this)->getNode(relative_path, false);
}
const SGPropertyNode *
SGPropertyNode::getNode (const char * relative_path, int index) const
{
return ((SGPropertyNode *)this)->getNode(relative_path, index, false);
}
////////////////////////////////////////////////////////////////////////
// Convenience methods using relative paths.
////////////////////////////////////////////////////////////////////////
/**
* Test whether another node has a value attached.
*/
bool
SGPropertyNode::hasValue (const char * relative_path) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? false : node->hasValue());
}
/**
* Get the value type for another node.
*/
props::Type
SGPropertyNode::getType (const char * relative_path) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? props::UNSPECIFIED : node->getType());
}
/**
* Get a bool value for another node.
*/
bool
SGPropertyNode::getBoolValue (const char * relative_path,
bool defaultValue) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? defaultValue : node->getBoolValue());
}
/**
* Get an int value for another node.
*/
int
SGPropertyNode::getIntValue (const char * relative_path,
int defaultValue) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? defaultValue : node->getIntValue());
}
/**
* Get a long value for another node.
*/
long
SGPropertyNode::getLongValue (const char * relative_path,
long defaultValue) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? defaultValue : node->getLongValue());
}
/**
* Get a float value for another node.
*/
float
SGPropertyNode::getFloatValue (const char * relative_path,
float defaultValue) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? defaultValue : node->getFloatValue());
}
/**
* Get a double value for another node.
*/
double
SGPropertyNode::getDoubleValue (const char * relative_path,
double defaultValue) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? defaultValue : node->getDoubleValue());
}
/**
* Get a string value for another node.
*/
const char *
SGPropertyNode::getStringValue (const char * relative_path,
const char * defaultValue) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? defaultValue : node->getStringValue());
}
/**
* Set a bool value for another node.
*/
bool
SGPropertyNode::setBoolValue (const char * relative_path, bool value)
{
return getNode(relative_path, true)->setBoolValue(value);
}
/**
* Set an int value for another node.
*/
bool
SGPropertyNode::setIntValue (const char * relative_path, int value)
{
return getNode(relative_path, true)->setIntValue(value);
}
/**
* Set a long value for another node.
*/
bool
SGPropertyNode::setLongValue (const char * relative_path, long value)
{
return getNode(relative_path, true)->setLongValue(value);
}
/**
* Set a float value for another node.
*/
bool
SGPropertyNode::setFloatValue (const char * relative_path, float value)
{
return getNode(relative_path, true)->setFloatValue(value);
}
/**
* Set a double value for another node.
*/
bool
SGPropertyNode::setDoubleValue (const char * relative_path, double value)
{
return getNode(relative_path, true)->setDoubleValue(value);
}
/**
* Set a string value for another node.
*/
bool
SGPropertyNode::setStringValue (const char * relative_path, const char * value)
{
return getNode(relative_path, true)->setStringValue(value);
}
/**
* Set an unknown value for another node.
*/
bool
SGPropertyNode::setUnspecifiedValue (const char * relative_path,
const char * value)
{
return getNode(relative_path, true)->setUnspecifiedValue(value);
}
/**
* Test whether another node is tied.
*/
bool
SGPropertyNode::isTied (const char * relative_path) const
{
const SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? false : node->isTied());
}
/**
* Tie a node reached by a relative path, creating it if necessary.
*/
bool
SGPropertyNode::tie (const char * relative_path,
const SGRawValue<bool> &rawValue,
bool useDefault)
{
return getNode(relative_path, true)->tie(rawValue, useDefault);
}
/**
* Tie a node reached by a relative path, creating it if necessary.
*/
bool
SGPropertyNode::tie (const char * relative_path,
const SGRawValue<int> &rawValue,
bool useDefault)
{
return getNode(relative_path, true)->tie(rawValue, useDefault);
}
/**
* Tie a node reached by a relative path, creating it if necessary.
*/
bool
SGPropertyNode::tie (const char * relative_path,
const SGRawValue<long> &rawValue,
bool useDefault)
{
return getNode(relative_path, true)->tie(rawValue, useDefault);
}
/**
* Tie a node reached by a relative path, creating it if necessary.
*/
bool
SGPropertyNode::tie (const char * relative_path,
const SGRawValue<float> &rawValue,
bool useDefault)
{
return getNode(relative_path, true)->tie(rawValue, useDefault);
}
/**
* Tie a node reached by a relative path, creating it if necessary.
*/
bool
SGPropertyNode::tie (const char * relative_path,
const SGRawValue<double> &rawValue,
bool useDefault)
{
return getNode(relative_path, true)->tie(rawValue, useDefault);
}
/**
* Tie a node reached by a relative path, creating it if necessary.
*/
bool
SGPropertyNode::tie (const char * relative_path,
const SGRawValue<const char *> &rawValue,
bool useDefault)
{
return getNode(relative_path, true)->tie(rawValue, useDefault);
}
/**
* Attempt to untie another node reached by a relative path.
*/
bool
SGPropertyNode::untie (const char * relative_path)
{
SGPropertyNode * node = getNode(relative_path);
return (node == 0 ? false : node->untie());
}
void
SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
bool initial)
{
if (_listeners == 0)
_listeners = new vector<SGPropertyChangeListener*>;
_listeners->push_back(listener);
listener->register_property(this);
if (initial)
listener->valueChanged(this);
}
void
SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
{
if (_listeners == 0)
return;
vector<SGPropertyChangeListener*>::iterator it =
find(_listeners->begin(), _listeners->end(), listener);
if (it != _listeners->end()) {
_listeners->erase(it);
listener->unregister_property(this);
if (_listeners->empty()) {
vector<SGPropertyChangeListener*>* tmp = _listeners;
_listeners = 0;
delete tmp;
}
}
}
void
SGPropertyNode::fireValueChanged ()
{
fireValueChanged(this);
}
void
SGPropertyNode::fireChildAdded (SGPropertyNode * child)
{
fireChildAdded(this, child);
}
void
SGPropertyNode::fireChildRemoved (SGPropertyNode * child)
{
fireChildRemoved(this, child);
}
void
SGPropertyNode::fireValueChanged (SGPropertyNode * node)
{
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->valueChanged(node);
}
}
if (_parent != 0)
_parent->fireValueChanged(node);
}
void
SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
SGPropertyNode * child)
{
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->childAdded(parent, child);
}
}
if (_parent != 0)
_parent->fireChildAdded(parent, child);
}
void
SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
SGPropertyNode * child)
{
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->childRemoved(parent, child);
}
}
if (_parent != 0)
_parent->fireChildRemoved(parent, child);
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGPropertyChangeListener.
////////////////////////////////////////////////////////////////////////
SGPropertyChangeListener::~SGPropertyChangeListener ()
{
for (int i = _properties.size() - 1; i >= 0; i--)
_properties[i]->removeChangeListener(this);
}
void
SGPropertyChangeListener::valueChanged (SGPropertyNode * node)
{
// NO-OP
}
void
SGPropertyChangeListener::childAdded (SGPropertyNode * node,
SGPropertyNode * child)
{
// NO-OP
}
void
SGPropertyChangeListener::childRemoved (SGPropertyNode * parent,
SGPropertyNode * child)
{
// NO-OP
}
void
SGPropertyChangeListener::register_property (SGPropertyNode * node)
{
_properties.push_back(node);
}
void
SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
{
vector<SGPropertyNode *>::iterator it =
find(_properties.begin(), _properties.end(), node);
if (it != _properties.end())
_properties.erase(it);
}
template<>
std::ostream& SGRawBase<SGVec3d>::printOn(std::ostream& stream) const
{
const SGVec3d vec
= static_cast<const SGRawValue<SGVec3d>*>(this)->getValue();
for (int i = 0; i < 3; ++i) {
stream << vec[i];
if (i < 2)
stream << ' ';
}
return stream;
}
namespace simgear
{
template<>
std::istream& readFrom<SGVec3d>(std::istream& stream, SGVec3d& result)
{
for (int i = 0; i < 3; ++i) {
stream >> result[i];
}
return stream;
}
}
template<>
std::ostream& SGRawBase<SGVec4d>::printOn(std::ostream& stream) const
{
const SGVec4d vec
= static_cast<const SGRawValue<SGVec4d>*>(this)->getValue();
for (int i = 0; i < 4; ++i) {
stream << vec[i];
if (i < 3)
stream << ' ';
}
return stream;
}
namespace simgear
{
template<>
std::istream& readFrom<SGVec4d>(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<bool>() == rhs.getValue<bool>();
case props::INT:
return lhs.getValue<int>() == rhs.getValue<int>();
case props::LONG:
return lhs.getValue<long>() == rhs.getValue<long>();
case props::FLOAT:
return lhs.getValue<float>() == rhs.getValue<float>();
case props::DOUBLE:
return lhs.getValue<double>() == rhs.getValue<double>();
case props::STRING:
case props::UNSPECIFIED:
return !strcmp(lhs.getStringValue(), rhs.getStringValue());
case props::VEC3D:
return lhs.getValue<SGVec3d>() == rhs.getValue<SGVec3d>();
case props::VEC4D:
return lhs.getValue<SGVec4d>() == rhs.getValue<SGVec4d>();
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<bool>());
case props::INT:
return hash_value(node.getValue<int>());
case props::LONG:
return hash_value(node.getValue<long>());
case props::FLOAT:
return hash_value(node.getValue<float>());
case props::DOUBLE:
return hash_value(node.getValue<double>());
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<SGVec3d>();
return hash_range(&val[0], &val[3]);
}
case props::VEC4D:
{
const SGVec4d val = node.getValue<SGVec4d>();
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