I have modified the SGPropertyNode interface to support node removal and address the issues you mention yesterday. I added the SGPropertyNode_ptr class that is a smart pointer that increment and decrement a counter in SGPropertyNode. The _children vector is changed to a vector<SGPropertyNode_ptr> in order for the node to hold a reference to its children. I also added a _removedChildren vector of the same type. When removeChild is called with the keep parameter set to true, the reference is removed from _children and inserted in _removedChildren, and the attribute REMOVED is set. When getChild is called with create set to true, the _children vector is searched, then the _removedChildren and then a new node is created. If a resurrected node is returned, the REMOVED bit is cleared. The static const int LAST_USED_ATTRIBUTE is also added. The footprint of this patch is light. SGPropertyNode is one int longer in size, SGPropertyNode_ptr is one pointer large without virtual functions. Some functions can be inlined if your current policy change. The FlightGear patch is to take account the change in the getChildren function that now returns a vector<SGPropertyNode_ptr>. If the removeChild functionnality is to be added in FlightGear, all those SGPropertyNode * floating around should be changed to SGPropertyNode_ptr.
2166 lines
42 KiB
C++
2166 lines
42 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$
|
|
|
|
#include "props.hxx"
|
|
|
|
#include <algorithm>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#if PROPS_STANDALONE
|
|
|
|
#include <iostream>
|
|
using std::cerr;
|
|
using std::endl;
|
|
using std::sort;
|
|
|
|
#else
|
|
|
|
#include <simgear/compiler.h>
|
|
#include <simgear/debug/logstream.hxx>
|
|
|
|
SG_USING_STD(sort);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// 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
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Default values for every type.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
const bool SGRawValue<bool>::DefaultValue = false;
|
|
const int SGRawValue<int>::DefaultValue = 0;
|
|
const long SGRawValue<long>::DefaultValue = 0L;
|
|
const float SGRawValue<float>::DefaultValue = 0.0;
|
|
const double SGRawValue<double>::DefaultValue = 0.0L;
|
|
const char * const SGRawValue<const char *>::DefaultValue = "";
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Local path normalization code.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* A component in a path.
|
|
*/
|
|
struct PathComponent
|
|
{
|
|
string name;
|
|
int index;
|
|
};
|
|
|
|
/**
|
|
* Parse the name for a path component.
|
|
*
|
|
* Name: [_a-zA-Z][-._a-zA-Z0-9]*
|
|
*/
|
|
static inline const string
|
|
parse_name (const string &path, int &i)
|
|
{
|
|
string name = "";
|
|
int max = path.size();
|
|
|
|
if (path[i] == '.') {
|
|
i++;
|
|
if (i < max && path[i] == '.') {
|
|
i++;
|
|
name = "..";
|
|
} else {
|
|
name = ".";
|
|
}
|
|
if (i < max && path[i] != '/')
|
|
throw string(string("Illegal character after ") + name);
|
|
}
|
|
|
|
else if (isalpha(path[i]) || path[i] == '_') {
|
|
name += path[i];
|
|
i++;
|
|
|
|
// The rules inside a name are a little
|
|
// less restrictive.
|
|
while (i < max) {
|
|
if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
|
|
path[i] == '-' || path[i] == '.') {
|
|
name += path[i];
|
|
} else if (path[i] == '[' || path[i] == '/') {
|
|
break;
|
|
} else {
|
|
throw string("name may contain only ._- and alphanumeric characters");
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
else {
|
|
if (name.size() == 0)
|
|
throw string("name must begin with alpha or '_'");
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse the optional integer index for a path component.
|
|
*
|
|
* Index: "[" [0-9]+ "]"
|
|
*/
|
|
static inline int
|
|
parse_index (const string &path, int &i)
|
|
{
|
|
int index = 0;
|
|
|
|
if (path[i] != '[')
|
|
return 0;
|
|
else
|
|
i++;
|
|
|
|
for (int max = path.size(); i < max; i++) {
|
|
if (isdigit(path[i])) {
|
|
index = (index * 10) + (path[i] - '0');
|
|
} else if (path[i] == ']') {
|
|
i++;
|
|
return index;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
throw string("unterminated index (looking for ']')");
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a single path component.
|
|
*
|
|
* Component: Name Index?
|
|
*/
|
|
static inline PathComponent
|
|
parse_component (const string &path, int &i)
|
|
{
|
|
PathComponent component;
|
|
component.name = parse_name(path, i);
|
|
if (component.name[0] != '.')
|
|
component.index = parse_index(path, i);
|
|
else
|
|
component.index = -1;
|
|
return component;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a path into its components.
|
|
*/
|
|
static void
|
|
parse_path (const string &path, vector<PathComponent> &components)
|
|
{
|
|
int pos = 0;
|
|
int max = path.size();
|
|
|
|
// Check for initial '/'
|
|
if (path[pos] == '/') {
|
|
PathComponent root;
|
|
root.name = "";
|
|
root.index = -1;
|
|
components.push_back(root);
|
|
pos++;
|
|
while (pos < max && path[pos] == '/')
|
|
pos++;
|
|
}
|
|
|
|
while (pos < max) {
|
|
components.push_back(parse_component(path, pos));
|
|
while (pos < max && path[pos] == '/')
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Other static utility functions.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
static char *
|
|
copy_string (const char * s)
|
|
{
|
|
// FIXME: potential buffer overflow.
|
|
// For some reason, strnlen and
|
|
// strncpy cause all kinds of crashes.
|
|
char * copy = new char[strlen(s) + 1];
|
|
strcpy(copy, s);
|
|
return copy;
|
|
}
|
|
|
|
static bool
|
|
compare_strings (const char * s1, const char * s2)
|
|
{
|
|
return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
|
|
}
|
|
|
|
/**
|
|
* Locate a child node by name and index.
|
|
*/
|
|
static int
|
|
find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
|
|
{
|
|
int nNodes = nodes.size();
|
|
for (int i = 0; i < nNodes; i++) {
|
|
SGPropertyNode * node = nodes[i];
|
|
if (compare_strings(node->getName(), name) && node->getIndex() == index)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Locate another node, given a relative path.
|
|
*/
|
|
static SGPropertyNode *
|
|
find_node (SGPropertyNode * current,
|
|
const vector<PathComponent> &components,
|
|
int position,
|
|
bool create)
|
|
{
|
|
// Run off the end of the list
|
|
if (current == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Success! This is the one we want.
|
|
else if (position >= (int)components.size()) {
|
|
return current;
|
|
}
|
|
|
|
// Empty component means root.
|
|
else if (components[position].name == "") {
|
|
return find_node(current->getRootNode(), components, position + 1, create);
|
|
}
|
|
|
|
// . means current directory
|
|
else if (components[position].name == ".") {
|
|
return find_node(current, components, position + 1, create);
|
|
}
|
|
|
|
// .. means parent directory
|
|
else if (components[position].name == "..") {
|
|
SGPropertyNode * parent = current->getParent();
|
|
if (parent == 0)
|
|
throw string("Attempt to move past root with '..'");
|
|
else
|
|
return find_node(parent, components, position + 1, create);
|
|
}
|
|
|
|
// Otherwise, a child name
|
|
else {
|
|
SGPropertyNode * child =
|
|
current->getChild(components[position].name.c_str(),
|
|
components[position].index,
|
|
create);
|
|
return find_node(child, components, position + 1, create);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Private methods from SGPropertyNode (may be inlined for speed).
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
inline bool
|
|
SGPropertyNode::get_bool () const
|
|
{
|
|
if (_tied)
|
|
return _value.bool_val->getValue();
|
|
else
|
|
return _local_val.bool_val;
|
|
}
|
|
|
|
inline int
|
|
SGPropertyNode::get_int () const
|
|
{
|
|
if (_tied)
|
|
return _value.int_val->getValue();
|
|
else
|
|
return _local_val.int_val;
|
|
}
|
|
|
|
inline long
|
|
SGPropertyNode::get_long () const
|
|
{
|
|
if (_tied)
|
|
return _value.long_val->getValue();
|
|
else
|
|
return _local_val.long_val;
|
|
}
|
|
|
|
inline float
|
|
SGPropertyNode::get_float () const
|
|
{
|
|
if (_tied)
|
|
return _value.float_val->getValue();
|
|
else
|
|
return _local_val.float_val;
|
|
}
|
|
|
|
inline double
|
|
SGPropertyNode::get_double () const
|
|
{
|
|
if (_tied)
|
|
return _value.double_val->getValue();
|
|
else
|
|
return _local_val.double_val;
|
|
}
|
|
|
|
inline const char *
|
|
SGPropertyNode::get_string () const
|
|
{
|
|
if (_tied)
|
|
return _value.string_val->getValue();
|
|
else
|
|
return _local_val.string_val;
|
|
}
|
|
|
|
inline bool
|
|
SGPropertyNode::set_bool (bool val)
|
|
{
|
|
if (_tied) {
|
|
return _value.bool_val->setValue(val);
|
|
} else {
|
|
_local_val.bool_val = val;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
SGPropertyNode::set_int (int val)
|
|
{
|
|
if (_tied) {
|
|
return _value.int_val->setValue(val);
|
|
} else {
|
|
_local_val.int_val = val;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
SGPropertyNode::set_long (long val)
|
|
{
|
|
if (_tied) {
|
|
return _value.long_val->setValue(val);
|
|
} else {
|
|
_local_val.long_val = val;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
SGPropertyNode::set_float (float val)
|
|
{
|
|
if (_tied) {
|
|
return _value.float_val->setValue(val);
|
|
} else {
|
|
_local_val.float_val = val;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
SGPropertyNode::set_double (double val)
|
|
{
|
|
if (_tied) {
|
|
return _value.double_val->setValue(val);
|
|
} else {
|
|
_local_val.double_val = val;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
SGPropertyNode::set_string (const char * val)
|
|
{
|
|
if (_tied) {
|
|
return _value.string_val->setValue(val);
|
|
} else {
|
|
delete [] _local_val.string_val;
|
|
_local_val.string_val = copy_string(val);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void
|
|
SGPropertyNode::clear_value ()
|
|
{
|
|
switch (_type) {
|
|
case NONE:
|
|
break;
|
|
case ALIAS:
|
|
_value.alias = 0;
|
|
break;
|
|
case BOOL:
|
|
if (_tied) {
|
|
delete _value.bool_val;
|
|
_value.bool_val = 0;
|
|
}
|
|
_local_val.bool_val = SGRawValue<bool>::DefaultValue;
|
|
break;
|
|
case INT:
|
|
if (_tied) {
|
|
delete _value.int_val;
|
|
_value.int_val = 0;
|
|
}
|
|
_local_val.int_val = SGRawValue<int>::DefaultValue;
|
|
break;
|
|
case LONG:
|
|
if (_tied) {
|
|
delete _value.long_val;
|
|
_value.long_val = 0L;
|
|
}
|
|
_local_val.long_val = SGRawValue<long>::DefaultValue;
|
|
break;
|
|
case FLOAT:
|
|
if (_tied) {
|
|
delete _value.float_val;
|
|
_value.float_val = 0;
|
|
}
|
|
_local_val.float_val = SGRawValue<float>::DefaultValue;
|
|
break;
|
|
case DOUBLE:
|
|
if (_tied) {
|
|
delete _value.double_val;
|
|
_value.double_val = 0;
|
|
}
|
|
_local_val.double_val = SGRawValue<double>::DefaultValue;
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
if (_tied) {
|
|
delete _value.string_val;
|
|
_value.string_val = 0;
|
|
} else {
|
|
delete [] _local_val.string_val;
|
|
}
|
|
_local_val.string_val = 0;
|
|
break;
|
|
}
|
|
_tied = false;
|
|
_type = NONE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the value as a string.
|
|
*/
|
|
const char *
|
|
SGPropertyNode::make_string () const
|
|
{
|
|
if (!getAttribute(READ))
|
|
return "";
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
return _value.alias->getStringValue();
|
|
case BOOL:
|
|
if (get_bool())
|
|
return "true";
|
|
else
|
|
return "false";
|
|
case INT:
|
|
sprintf(_buffer, "%d", get_int());
|
|
return _buffer;
|
|
case LONG:
|
|
sprintf(_buffer, "%ld", get_long());
|
|
return _buffer;
|
|
case FLOAT:
|
|
sprintf(_buffer, "%f", get_float());
|
|
return _buffer;
|
|
case DOUBLE:
|
|
sprintf(_buffer, "%f", get_double());
|
|
return _buffer;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
return get_string();
|
|
case NONE:
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trace a write access for a property.
|
|
*/
|
|
void
|
|
SGPropertyNode::trace_write () const
|
|
{
|
|
#if PROPS_STANDALONE
|
|
cerr << "TRACE: Write node " << getPath () << ", value\""
|
|
<< make_string() << '"' << endl;
|
|
#else
|
|
SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
|
|
<< ", value\"" << make_string() << '"');
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* 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_INFO, "TRACE: Read node " << getPath()
|
|
<< ", value \"" << make_string() << '"');
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Increment reference counter
|
|
*/
|
|
void
|
|
SGPropertyNode::incrementRef()
|
|
{
|
|
++_count;
|
|
}
|
|
|
|
/**
|
|
* Decrement reference counter
|
|
*/
|
|
int
|
|
SGPropertyNode::decrementRef()
|
|
{
|
|
return --_count;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Public methods from SGPropertyNode.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Last used attribute
|
|
* Update as needed when enum Attribute is changed
|
|
*/
|
|
const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE;
|
|
|
|
/**
|
|
* Default constructor: always creates a root node.
|
|
*/
|
|
SGPropertyNode::SGPropertyNode ()
|
|
: _name(copy_string("")),
|
|
_index(0),
|
|
_parent(0),
|
|
_path_cache(0),
|
|
_type(NONE),
|
|
_tied(false),
|
|
_attr(READ|WRITE),
|
|
_count(0)
|
|
{
|
|
_local_val.string_val = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy constructor.
|
|
*/
|
|
SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
|
|
: _index(node._index),
|
|
_parent(0), // don't copy the parent
|
|
_path_cache(0),
|
|
_type(node._type),
|
|
_tied(node._tied),
|
|
_attr(node._attr),
|
|
_count(0)
|
|
{
|
|
_name = copy_string(node._name);
|
|
_local_val.string_val = 0;
|
|
switch (_type) {
|
|
case NONE:
|
|
break;
|
|
case ALIAS:
|
|
_value.alias = node._value.alias;
|
|
_tied = false;
|
|
break;
|
|
case BOOL:
|
|
if (_tied) {
|
|
_tied = true;
|
|
_value.bool_val = node._value.bool_val->clone();
|
|
} else {
|
|
_tied = false;
|
|
set_bool(node.get_bool());
|
|
}
|
|
break;
|
|
case INT:
|
|
if (_tied) {
|
|
_tied = true;
|
|
_value.int_val = node._value.int_val->clone();
|
|
} else {
|
|
_tied = false;
|
|
set_int(node.get_int());
|
|
}
|
|
break;
|
|
case LONG:
|
|
if (_tied) {
|
|
_tied = true;
|
|
_value.long_val = node._value.long_val->clone();
|
|
} else {
|
|
_tied = false;
|
|
set_long(node.get_long());
|
|
}
|
|
break;
|
|
case FLOAT:
|
|
if (_tied) {
|
|
_tied = true;
|
|
_value.float_val = node._value.float_val->clone();
|
|
} else {
|
|
_tied = false;
|
|
set_float(node.get_float());
|
|
}
|
|
break;
|
|
case DOUBLE:
|
|
if (_tied) {
|
|
_tied = true;
|
|
_value.double_val = node._value.double_val->clone();
|
|
} else {
|
|
_tied = false;
|
|
set_double(node.get_double());
|
|
}
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
if (_tied) {
|
|
_tied = true;
|
|
_value.string_val = node._value.string_val->clone();
|
|
} else {
|
|
_tied = false;
|
|
set_string(node.get_string());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Convenience constructor.
|
|
*/
|
|
SGPropertyNode::SGPropertyNode (const char * name,
|
|
int index,
|
|
SGPropertyNode * parent)
|
|
: _index(index),
|
|
_parent(parent),
|
|
_path_cache(0),
|
|
_type(NONE),
|
|
_tied(false),
|
|
_attr(READ|WRITE),
|
|
_count(0)
|
|
{
|
|
_name = copy_string(name);
|
|
_local_val.string_val = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Destructor.
|
|
*/
|
|
SGPropertyNode::~SGPropertyNode ()
|
|
{
|
|
delete [] _name;
|
|
delete _path_cache;
|
|
clear_value();
|
|
}
|
|
|
|
|
|
/**
|
|
* Alias to another node.
|
|
*/
|
|
bool
|
|
SGPropertyNode::alias (SGPropertyNode * target)
|
|
{
|
|
if (target == 0 || _type == ALIAS || _tied)
|
|
return false;
|
|
clear_value();
|
|
_value.alias = target;
|
|
_type = ALIAS;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Alias to another node by path.
|
|
*/
|
|
bool
|
|
SGPropertyNode::alias (const char * path)
|
|
{
|
|
return alias(getNode(path, true));
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove an alias.
|
|
*/
|
|
bool
|
|
SGPropertyNode::unalias ()
|
|
{
|
|
if (_type != ALIAS)
|
|
return false;
|
|
_type = NONE;
|
|
_value.alias = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the target of an alias.
|
|
*/
|
|
SGPropertyNode *
|
|
SGPropertyNode::getAliasTarget ()
|
|
{
|
|
return (_type == ALIAS ? _value.alias : 0);
|
|
}
|
|
|
|
|
|
const SGPropertyNode *
|
|
SGPropertyNode::getAliasTarget () const
|
|
{
|
|
return (_type == ALIAS ? _value.alias : 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a non-const child by index.
|
|
*/
|
|
SGPropertyNode *
|
|
SGPropertyNode::getChild (int position)
|
|
{
|
|
if (position >= 0 && position < nChildren())
|
|
return _children[position];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a const child by index.
|
|
*/
|
|
const SGPropertyNode *
|
|
SGPropertyNode::getChild (int position) const
|
|
{
|
|
if (position >= 0 && position < nChildren())
|
|
return _children[position];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a non-const child by name and index, creating if necessary.
|
|
*/
|
|
SGPropertyNode *
|
|
SGPropertyNode::getChild (const char * name, int index, bool create)
|
|
{
|
|
int pos = find_child(name, index, _children);
|
|
if (pos >= 0) {
|
|
return _children[pos];
|
|
} else if (create) {
|
|
SGPropertyNode_ptr node;
|
|
pos = find_child(name, index, _removedChildren);
|
|
if (pos >= 0) {
|
|
std::vector<SGPropertyNode_ptr>::iterator it = _removedChildren.begin();
|
|
it += pos;
|
|
node = _removedChildren[pos];
|
|
_removedChildren.erase(it);
|
|
node->setAttribute(REMOVED, false);
|
|
} else {
|
|
node = new SGPropertyNode(name, index, this);
|
|
}
|
|
_children.push_back(node);
|
|
return _children[_children.size()-1];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a const child by name and index.
|
|
*/
|
|
const SGPropertyNode *
|
|
SGPropertyNode::getChild (const char * name, int index) const
|
|
{
|
|
int pos = find_child(name, index, _children);
|
|
if (pos >= 0)
|
|
return _children[pos];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get all children with the same name (but different indices).
|
|
*/
|
|
vector<SGPropertyNode_ptr>
|
|
SGPropertyNode::getChildren (const char * name) const
|
|
{
|
|
vector<SGPropertyNode_ptr> 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 a child node
|
|
*/
|
|
SGPropertyNode_ptr
|
|
SGPropertyNode::removeChild (const char * name, int index, bool keep)
|
|
{
|
|
SGPropertyNode_ptr ret;
|
|
int pos = find_child(name, index, _children);
|
|
if (pos >= 0) {
|
|
std::vector<SGPropertyNode_ptr>::iterator it = _children.begin();
|
|
it += pos;
|
|
SGPropertyNode_ptr node = _children[pos];
|
|
_children.erase(it);
|
|
if (keep) {
|
|
_removedChildren.push_back(node);
|
|
}
|
|
node->setAttribute(REMOVED, true);
|
|
ret = node;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
const char *
|
|
SGPropertyNode::getPath (bool simplify) const
|
|
{
|
|
if (_parent == 0)
|
|
return "";
|
|
|
|
string path = _parent->getPath(simplify);
|
|
path += '/';
|
|
path += _name;
|
|
if (_index != 0 || !simplify) {
|
|
char buffer[128];
|
|
sprintf(buffer, "[%d]", _index);
|
|
path += buffer;
|
|
}
|
|
return path.c_str();
|
|
}
|
|
|
|
SGPropertyNode::Type
|
|
SGPropertyNode::getType () const
|
|
{
|
|
if (_type == ALIAS)
|
|
return _value.alias->getType();
|
|
else
|
|
return _type;
|
|
}
|
|
|
|
|
|
bool
|
|
SGPropertyNode::getBoolValue () const
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == BOOL)
|
|
return get_bool();
|
|
|
|
if (getAttribute(TRACE_READ))
|
|
trace_read();
|
|
if (!getAttribute(READ))
|
|
return SGRawValue<bool>::DefaultValue;
|
|
switch (_type) {
|
|
case ALIAS:
|
|
return _value.alias->getBoolValue();
|
|
case BOOL:
|
|
return get_bool();
|
|
case INT:
|
|
return get_int() == 0 ? false : true;
|
|
case LONG:
|
|
return get_long() == 0L ? false : true;
|
|
case FLOAT:
|
|
return get_float() == 0.0 ? false : true;
|
|
case DOUBLE:
|
|
return get_double() == 0.0L ? false : true;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
|
|
case NONE:
|
|
default:
|
|
return SGRawValue<bool>::DefaultValue;
|
|
}
|
|
}
|
|
|
|
int
|
|
SGPropertyNode::getIntValue () const
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == INT)
|
|
return get_int();
|
|
|
|
if (getAttribute(TRACE_READ))
|
|
trace_read();
|
|
if (!getAttribute(READ))
|
|
return SGRawValue<int>::DefaultValue;
|
|
switch (_type) {
|
|
case ALIAS:
|
|
return _value.alias->getIntValue();
|
|
case BOOL:
|
|
return int(get_bool());
|
|
case INT:
|
|
return get_int();
|
|
case LONG:
|
|
return int(get_long());
|
|
case FLOAT:
|
|
return int(get_float());
|
|
case DOUBLE:
|
|
return int(get_double());
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
return atoi(get_string());
|
|
case NONE:
|
|
default:
|
|
return SGRawValue<int>::DefaultValue;
|
|
}
|
|
}
|
|
|
|
long
|
|
SGPropertyNode::getLongValue () const
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == LONG)
|
|
return get_long();
|
|
|
|
if (getAttribute(TRACE_READ))
|
|
trace_read();
|
|
if (!getAttribute(READ))
|
|
return SGRawValue<long>::DefaultValue;
|
|
switch (_type) {
|
|
case ALIAS:
|
|
return _value.alias->getLongValue();
|
|
case BOOL:
|
|
return long(get_bool());
|
|
case INT:
|
|
return long(get_int());
|
|
case LONG:
|
|
return get_long();
|
|
case FLOAT:
|
|
return long(get_float());
|
|
case DOUBLE:
|
|
return long(get_double());
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
return strtol(get_string(), 0, 0);
|
|
case NONE:
|
|
default:
|
|
return SGRawValue<long>::DefaultValue;
|
|
}
|
|
}
|
|
|
|
float
|
|
SGPropertyNode::getFloatValue () const
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == FLOAT)
|
|
return get_float();
|
|
|
|
if (getAttribute(TRACE_READ))
|
|
trace_read();
|
|
if (!getAttribute(READ))
|
|
return SGRawValue<float>::DefaultValue;
|
|
switch (_type) {
|
|
case ALIAS:
|
|
return _value.alias->getFloatValue();
|
|
case BOOL:
|
|
return float(get_bool());
|
|
case INT:
|
|
return float(get_int());
|
|
case LONG:
|
|
return float(get_long());
|
|
case FLOAT:
|
|
return get_float();
|
|
case DOUBLE:
|
|
return float(get_double());
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
return atof(get_string());
|
|
case NONE:
|
|
default:
|
|
return SGRawValue<float>::DefaultValue;
|
|
}
|
|
}
|
|
|
|
double
|
|
SGPropertyNode::getDoubleValue () const
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == DOUBLE)
|
|
return get_double();
|
|
|
|
if (getAttribute(TRACE_READ))
|
|
trace_read();
|
|
if (!getAttribute(READ))
|
|
return SGRawValue<double>::DefaultValue;
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
return _value.alias->getDoubleValue();
|
|
case BOOL:
|
|
return double(get_bool());
|
|
case INT:
|
|
return double(get_int());
|
|
case LONG:
|
|
return double(get_long());
|
|
case FLOAT:
|
|
return double(get_float());
|
|
case DOUBLE:
|
|
return get_double();
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
return strtod(get_string(), 0);
|
|
case NONE:
|
|
default:
|
|
return SGRawValue<double>::DefaultValue;
|
|
}
|
|
}
|
|
|
|
const char *
|
|
SGPropertyNode::getStringValue () const
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == STRING)
|
|
return get_string();
|
|
|
|
if (getAttribute(TRACE_READ))
|
|
trace_read();
|
|
if (!getAttribute(READ))
|
|
return SGRawValue<const char *>::DefaultValue;
|
|
return make_string();
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::setBoolValue (bool value)
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == BOOL)
|
|
return set_bool(value);
|
|
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE || _type == UNSPECIFIED) {
|
|
clear_value();
|
|
_tied = false;
|
|
_type = BOOL;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setBoolValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool(value);
|
|
break;
|
|
case INT:
|
|
result = set_int(int(value));
|
|
break;
|
|
case LONG:
|
|
result = set_long(long(value));
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(float(value));
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(double(value));
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
result = set_string(value ? "true" : "false");
|
|
break;
|
|
case 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 == INT)
|
|
return set_int(value);
|
|
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE || _type == UNSPECIFIED) {
|
|
clear_value();
|
|
_type = INT;
|
|
_local_val.int_val = 0;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setIntValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool(value == 0 ? false : true);
|
|
break;
|
|
case INT:
|
|
result = set_int(value);
|
|
break;
|
|
case LONG:
|
|
result = set_long(long(value));
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(float(value));
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(double(value));
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED: {
|
|
char buf[128];
|
|
sprintf(buf, "%d", value);
|
|
result = set_string(buf);
|
|
break;
|
|
}
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (getAttribute(TRACE_WRITE))
|
|
trace_write();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::setLongValue (long value)
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == LONG)
|
|
return set_long(value);
|
|
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE || _type == UNSPECIFIED) {
|
|
clear_value();
|
|
_type = LONG;
|
|
_local_val.long_val = 0L;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setLongValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool(value == 0L ? false : true);
|
|
break;
|
|
case INT:
|
|
result = set_int(int(value));
|
|
break;
|
|
case LONG:
|
|
result = set_long(value);
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(float(value));
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(double(value));
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED: {
|
|
char buf[128];
|
|
sprintf(buf, "%ld", value);
|
|
result = set_string(buf);
|
|
break;
|
|
}
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (getAttribute(TRACE_WRITE))
|
|
trace_write();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::setFloatValue (float value)
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == FLOAT)
|
|
return set_float(value);
|
|
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE || _type == UNSPECIFIED) {
|
|
clear_value();
|
|
_type = FLOAT;
|
|
_local_val.float_val = 0;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setFloatValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool(value == 0.0 ? false : true);
|
|
break;
|
|
case INT:
|
|
result = set_int(int(value));
|
|
break;
|
|
case LONG:
|
|
result = set_long(long(value));
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(value);
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(double(value));
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED: {
|
|
char buf[128];
|
|
sprintf(buf, "%f", value);
|
|
result = set_string(buf);
|
|
break;
|
|
}
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (getAttribute(TRACE_WRITE))
|
|
trace_write();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::setDoubleValue (double value)
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == DOUBLE)
|
|
return set_double(value);
|
|
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE || _type == UNSPECIFIED) {
|
|
clear_value();
|
|
_local_val.double_val = value;
|
|
_type = DOUBLE;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setDoubleValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool(value == 0.0L ? false : true);
|
|
break;
|
|
case INT:
|
|
result = set_int(int(value));
|
|
break;
|
|
case LONG:
|
|
result = set_long(long(value));
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(float(value));
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(value);
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED: {
|
|
char buf[128];
|
|
sprintf(buf, "%f", value);
|
|
result = set_string(buf);
|
|
break;
|
|
}
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (getAttribute(TRACE_WRITE))
|
|
trace_write();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::setStringValue (const char * value)
|
|
{
|
|
// Shortcut for common case
|
|
if (_attr == (READ|WRITE) && _type == STRING)
|
|
return set_string(value);
|
|
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE || _type == UNSPECIFIED) {
|
|
clear_value();
|
|
_type = STRING;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setStringValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool((compare_strings(value, "true")
|
|
|| atoi(value)) ? true : false);
|
|
break;
|
|
case INT:
|
|
result = set_int(atoi(value));
|
|
break;
|
|
case LONG:
|
|
result = set_long(strtol(value, 0, 0));
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(atof(value));
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(strtod(value, 0));
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
result = set_string(value);
|
|
break;
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (getAttribute(TRACE_WRITE))
|
|
trace_write();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::setUnspecifiedValue (const char * value)
|
|
{
|
|
bool result = false;
|
|
TEST_WRITE;
|
|
if (_type == NONE) {
|
|
clear_value();
|
|
_type = UNSPECIFIED;
|
|
}
|
|
|
|
switch (_type) {
|
|
case ALIAS:
|
|
result = _value.alias->setUnspecifiedValue(value);
|
|
break;
|
|
case BOOL:
|
|
result = set_bool((compare_strings(value, "true")
|
|
|| atoi(value)) ? true : false);
|
|
break;
|
|
case INT:
|
|
result = set_int(atoi(value));
|
|
break;
|
|
case LONG:
|
|
result = set_long(strtol(value, 0, 0));
|
|
break;
|
|
case FLOAT:
|
|
result = set_float(atof(value));
|
|
break;
|
|
case DOUBLE:
|
|
result = set_double(strtod(value, 0));
|
|
break;
|
|
case STRING:
|
|
case UNSPECIFIED:
|
|
result = set_string(value);
|
|
break;
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (getAttribute(TRACE_WRITE))
|
|
trace_write();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::tie (const SGRawValue<bool> &rawValue, bool useDefault)
|
|
{
|
|
if (_type == ALIAS || _tied)
|
|
return false;
|
|
|
|
useDefault = useDefault && hasValue();
|
|
bool old_val = false;
|
|
if (useDefault)
|
|
old_val = getBoolValue();
|
|
|
|
clear_value();
|
|
_type = BOOL;
|
|
_tied = true;
|
|
_value.bool_val = rawValue.clone();
|
|
|
|
if (useDefault)
|
|
setBoolValue(old_val);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::tie (const SGRawValue<int> &rawValue, bool useDefault)
|
|
{
|
|
if (_type == ALIAS || _tied)
|
|
return false;
|
|
|
|
useDefault = useDefault && hasValue();
|
|
int old_val = 0;
|
|
if (useDefault)
|
|
old_val = getIntValue();
|
|
|
|
clear_value();
|
|
_type = INT;
|
|
_tied = true;
|
|
_value.int_val = rawValue.clone();
|
|
|
|
if (useDefault)
|
|
setIntValue(old_val);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::tie (const SGRawValue<long> &rawValue, bool useDefault)
|
|
{
|
|
if (_type == ALIAS || _tied)
|
|
return false;
|
|
|
|
useDefault = useDefault && hasValue();
|
|
long old_val = 0;
|
|
if (useDefault)
|
|
old_val = getLongValue();
|
|
|
|
clear_value();
|
|
_type = LONG;
|
|
_tied = true;
|
|
_value.long_val = rawValue.clone();
|
|
|
|
if (useDefault)
|
|
setLongValue(old_val);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::tie (const SGRawValue<float> &rawValue, bool useDefault)
|
|
{
|
|
if (_type == ALIAS || _tied)
|
|
return false;
|
|
|
|
useDefault = useDefault && hasValue();
|
|
float old_val = 0.0;
|
|
if (useDefault)
|
|
old_val = getFloatValue();
|
|
|
|
clear_value();
|
|
_type = FLOAT;
|
|
_tied = true;
|
|
_value.float_val = rawValue.clone();
|
|
|
|
if (useDefault)
|
|
setFloatValue(old_val);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::tie (const SGRawValue<double> &rawValue, bool useDefault)
|
|
{
|
|
if (_type == ALIAS || _tied)
|
|
return false;
|
|
|
|
useDefault = useDefault && hasValue();
|
|
double old_val = 0.0;
|
|
if (useDefault)
|
|
old_val = getDoubleValue();
|
|
|
|
clear_value();
|
|
_type = DOUBLE;
|
|
_tied = true;
|
|
_value.double_val = rawValue.clone();
|
|
|
|
if (useDefault)
|
|
setDoubleValue(old_val);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::tie (const SGRawValue<const char *> &rawValue, bool useDefault)
|
|
{
|
|
if (_type == ALIAS || _tied)
|
|
return false;
|
|
|
|
useDefault = useDefault && hasValue();
|
|
string old_val;
|
|
if (useDefault)
|
|
old_val = getStringValue();
|
|
|
|
clear_value();
|
|
_type = STRING;
|
|
_tied = true;
|
|
_value.string_val = rawValue.clone();
|
|
|
|
if (useDefault)
|
|
setStringValue(old_val.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
SGPropertyNode::untie ()
|
|
{
|
|
if (!_tied)
|
|
return false;
|
|
|
|
switch (_type) {
|
|
case BOOL: {
|
|
bool val = getBoolValue();
|
|
clear_value();
|
|
_type = BOOL;
|
|
_local_val.bool_val = val;
|
|
break;
|
|
}
|
|
case INT: {
|
|
int val = getIntValue();
|
|
clear_value();
|
|
_type = INT;
|
|
_local_val.int_val = val;
|
|
break;
|
|
}
|
|
case LONG: {
|
|
long val = getLongValue();
|
|
clear_value();
|
|
_type = LONG;
|
|
_local_val.long_val = val;
|
|
break;
|
|
}
|
|
case FLOAT: {
|
|
float val = getFloatValue();
|
|
clear_value();
|
|
_type = FLOAT;
|
|
_local_val.float_val = val;
|
|
break;
|
|
}
|
|
case DOUBLE: {
|
|
double val = getDoubleValue();
|
|
clear_value();
|
|
_type = DOUBLE;
|
|
_local_val.double_val = val;
|
|
break;
|
|
}
|
|
case STRING:
|
|
case UNSPECIFIED: {
|
|
string val = getStringValue();
|
|
clear_value();
|
|
_type = STRING;
|
|
_local_val.string_val = copy_string(val.c_str());
|
|
break;
|
|
}
|
|
case NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_tied = false;
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (_path_cache == 0)
|
|
_path_cache = new hash_table;
|
|
|
|
SGPropertyNode * result = _path_cache->get(relative_path);
|
|
if (result == 0) {
|
|
vector<PathComponent> components;
|
|
parse_path(relative_path, components);
|
|
result = find_node(this, components, 0, create);
|
|
if (result != 0)
|
|
_path_cache->put(relative_path, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
SGPropertyNode *
|
|
SGPropertyNode::getNode (const char * relative_path, int index, bool create)
|
|
{
|
|
vector<PathComponent> components;
|
|
parse_path(relative_path, components);
|
|
if (components.size() > 0)
|
|
components[components.size()-1].index = index;
|
|
return find_node(this, components, 0, create);
|
|
}
|
|
|
|
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.
|
|
*/
|
|
SGPropertyNode::Type
|
|
SGPropertyNode::getType (const char * relative_path) const
|
|
{
|
|
const SGPropertyNode * node = getNode(relative_path);
|
|
return (node == 0 ? UNSPECIFIED : (Type)(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());
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Simplified hash table for caching paths.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
#define HASH_TABLE_SIZE 199
|
|
|
|
SGPropertyNode::hash_table::entry::entry ()
|
|
: _key(0),
|
|
_value(0)
|
|
{
|
|
}
|
|
|
|
SGPropertyNode::hash_table::entry::~entry ()
|
|
{
|
|
// Don't delete the value; we don't own
|
|
// the pointer.
|
|
delete [] _key;
|
|
}
|
|
|
|
void
|
|
SGPropertyNode::hash_table::entry::set_key (const char * key)
|
|
{
|
|
_key = copy_string(key);
|
|
}
|
|
|
|
void
|
|
SGPropertyNode::hash_table::entry::set_value (SGPropertyNode * value)
|
|
{
|
|
_value = value;
|
|
}
|
|
|
|
SGPropertyNode::hash_table::bucket::bucket ()
|
|
: _length(0),
|
|
_entries(0)
|
|
{
|
|
}
|
|
|
|
SGPropertyNode::hash_table::bucket::~bucket ()
|
|
{
|
|
for (int i = 0; i < _length; i++)
|
|
delete _entries[i];
|
|
}
|
|
|
|
SGPropertyNode::hash_table::entry *
|
|
SGPropertyNode::hash_table::bucket::get_entry (const char * key, bool create)
|
|
{
|
|
int i;
|
|
for (i = 0; i < _length; i++) {
|
|
if (!strcmp(_entries[i]->get_key(), key))
|
|
return _entries[i];
|
|
}
|
|
if (create) {
|
|
entry ** new_entries = new entry*[_length+1];
|
|
for (i = 0; i < _length; i++) {
|
|
new_entries[i] = _entries[i];
|
|
}
|
|
delete [] _entries;
|
|
_entries = new_entries;
|
|
_entries[_length] = new entry;
|
|
_entries[_length]->set_key(key);
|
|
_length++;
|
|
return _entries[_length - 1];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
SGPropertyNode::hash_table::hash_table ()
|
|
: _data_length(0),
|
|
_data(0)
|
|
{
|
|
}
|
|
|
|
SGPropertyNode::hash_table::~hash_table ()
|
|
{
|
|
for (unsigned int i = 0; i < _data_length; i++)
|
|
delete _data[i];
|
|
}
|
|
|
|
SGPropertyNode *
|
|
SGPropertyNode::hash_table::get (const char * key)
|
|
{
|
|
if (_data_length == 0)
|
|
return 0;
|
|
unsigned int index = hashcode(key) % _data_length;
|
|
if (_data[index] == 0)
|
|
return 0;
|
|
entry * e = _data[index]->get_entry(key);
|
|
if (e == 0)
|
|
return 0;
|
|
else
|
|
return e->get_value();
|
|
}
|
|
|
|
void
|
|
SGPropertyNode::hash_table::put (const char * key, SGPropertyNode * value)
|
|
{
|
|
if (_data_length == 0) {
|
|
_data = new bucket*[HASH_TABLE_SIZE];
|
|
_data_length = HASH_TABLE_SIZE;
|
|
for (unsigned int i = 0; i < HASH_TABLE_SIZE; i++)
|
|
_data[i] = 0;
|
|
}
|
|
unsigned int index = hashcode(key) % _data_length;
|
|
if (_data[index] == 0) {
|
|
_data[index] = new bucket;
|
|
}
|
|
entry * e = _data[index]->get_entry(key, true);
|
|
e->set_value(value);
|
|
}
|
|
|
|
unsigned int
|
|
SGPropertyNode::hash_table::hashcode (const char * key)
|
|
{
|
|
unsigned int hash = 0;
|
|
while (*key != 0) {
|
|
hash = 31 * hash + *key;
|
|
key++;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Default constructor
|
|
*/
|
|
SGPropertyNode_ptr::SGPropertyNode_ptr()
|
|
{
|
|
_ptr = 0;
|
|
}
|
|
|
|
/**
|
|
* Copy constructor
|
|
*/
|
|
SGPropertyNode_ptr::SGPropertyNode_ptr( const SGPropertyNode_ptr &r )
|
|
{
|
|
_ptr = r._ptr;
|
|
if (_ptr)
|
|
_ptr->incrementRef();
|
|
}
|
|
|
|
/**
|
|
* Constructor from a pointer to a node
|
|
*/
|
|
SGPropertyNode_ptr::SGPropertyNode_ptr( SGPropertyNode *p )
|
|
{
|
|
_ptr = p;
|
|
if (_ptr)
|
|
_ptr->incrementRef();
|
|
}
|
|
|
|
/**
|
|
* Destructor
|
|
*/
|
|
SGPropertyNode_ptr::~SGPropertyNode_ptr()
|
|
{
|
|
if (_ptr && _ptr->decrementRef() == 0)
|
|
delete _ptr;
|
|
}
|
|
|
|
/**
|
|
* Assignement operator
|
|
*/
|
|
SGPropertyNode_ptr &
|
|
SGPropertyNode_ptr::operator=( const SGPropertyNode_ptr &r )
|
|
{
|
|
if (_ptr && _ptr->decrementRef() == 0)
|
|
delete _ptr;
|
|
_ptr = r._ptr;
|
|
if (_ptr)
|
|
_ptr->incrementRef();
|
|
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Pointer access operator
|
|
*/
|
|
SGPropertyNode *
|
|
SGPropertyNode_ptr::operator->()
|
|
{
|
|
return _ptr;
|
|
}
|
|
|
|
/**
|
|
* Pointer access operator (const)
|
|
*/
|
|
const SGPropertyNode *
|
|
SGPropertyNode_ptr::operator->() const
|
|
{
|
|
return _ptr;
|
|
}
|
|
|
|
/**
|
|
* Conversion to SGPropertyNode * operator
|
|
*/
|
|
SGPropertyNode_ptr::operator SGPropertyNode *()
|
|
{
|
|
return _ptr;
|
|
}
|
|
|
|
/**
|
|
* Conversion to const SGPropertyNode * operator
|
|
*/
|
|
SGPropertyNode_ptr::operator const SGPropertyNode *() const
|
|
{
|
|
return _ptr;
|
|
}
|
|
|
|
/**
|
|
* Validity test
|
|
*/
|
|
bool
|
|
SGPropertyNode_ptr::valid() const
|
|
{
|
|
return _ptr != 0;
|
|
}
|
|
|
|
|
|
// end of props.cxx
|