#include "props.hxx"
#include <algorithm>
+
+#include <sstream>
#include <stdio.h>
#include <string.h>
#if PROPS_STANDALONE
-
#include <iostream>
-using std::cerr;
-using std::endl;
-using std::find;
-using std::sort;
-using std::vector;
-
#else
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
-SG_USING_STD(sort);
-SG_USING_STD(find);
-SG_USING_STD(vector);
-
-#ifdef _MSC_VER
-// MSVC is buggy, and needs something strange here
+#if ( _MSC_VER == 1200 )
+// MSVC 6 is buggy, and needs something strange here
SG_USING_STD(vector<SGPropertyNode_ptr>);
SG_USING_STD(vector<SGPropertyChangeListener *>);
SG_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;
\f
name = ".";
}
if (i < max && path[i] != '/')
- throw string("Illegal character after " + name);
+ throw string("illegal character after " + name);
}
else if (isalpha(path[i]) || path[i] == '_') {
* Locate a child node by name and index.
*/
static int
-find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
+find_child (const char * name, int index, const vector<SGPropertyNode_ptr>& nodes)
{
int nNodes = nodes.size();
for (int i = 0; i < nNodes; i++) {
// Success! This is the one we want.
else if (position >= (int)components.size()) {
- return current;
+ return (current->getAttribute(SGPropertyNode::REMOVED) ? 0 : current);
}
// Empty component means root.
else if (components[position].name == "..") {
SGPropertyNode * parent = current->getParent();
if (parent == 0)
- throw string("Attempt to move past root with '..'");
+ throw string("attempt to move past root with '..'");
else
return find_node(parent, components, position + 1, create);
}
}
void
-SGPropertyNode::clear_value ()
+SGPropertyNode::clearValue ()
{
switch (_type) {
case NONE:
else
return "false";
case INT:
- sprintf(_buffer, "%d", get_int());
- return _buffer;
+ {
+ stringstream sstr;
+ sstr << get_int();
+ _buffer = sstr.str();
+ return _buffer.c_str();
+ }
case LONG:
- sprintf(_buffer, "%ld", get_long());
- return _buffer;
+ {
+ stringstream sstr;
+ sstr << get_long();
+ _buffer = sstr.str();
+ return _buffer.c_str();
+ }
case FLOAT:
- sprintf(_buffer, "%f", get_float());
- return _buffer;
+ {
+ stringstream sstr;
+ sstr << get_float();
+ _buffer = sstr.str();
+ return _buffer.c_str();
+ }
case DOUBLE:
- sprintf(_buffer, "%f", get_double());
- return _buffer;
+ {
+ stringstream sstr;
+ sstr.precision( 10 );
+ sstr << get_double();
+ _buffer = sstr.str();
+ return _buffer.c_str();
+ }
case STRING:
case UNSPECIFIED:
return get_string();
SGPropertyNode::trace_write () const
{
#if PROPS_STANDALONE
- cerr << "TRACE: Write node " << getPath () << ", value\""
+ cerr << "TRACE: Write node " << getPath () << ", value \""
<< make_string() << '"' << endl;
#else
- SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
- << ", value\"" << make_string() << '"');
+ SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << getPath()
+ << ", value \"" << make_string() << '"');
#endif
}
cerr << "TRACE: Write node " << getPath () << ", value \""
<< make_string() << '"' << endl;
#else
- SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath()
+ SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << getPath()
<< ", value \"" << make_string() << '"');
#endif
}
-/**
- * Increment reference counter
- */
-void
-SGPropertyNode::incrementRef()
-{
- ++_count;
-}
-
-/**
- * Decrement reference counter
- */
-int
-SGPropertyNode::decrementRef()
-{
- return --_count;
-}
-
-
\f
////////////////////////////////////////////////////////////////////////
// Public methods from SGPropertyNode.
* Last used attribute
* Update as needed when enum Attribute is changed
*/
-const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE;
+const int SGPropertyNode::LAST_USED_ATTRIBUTE = USERARCHIVE;
/**
* Default constructor: always creates a root node.
*/
SGPropertyNode::SGPropertyNode ()
- : _name(copy_string("")),
- _display_name(0),
- _index(0),
+ : _index(0),
_parent(0),
- _path(0),
_path_cache(0),
_type(NONE),
_tied(false),
_attr(READ|WRITE),
- _count(0),
_listeners(0)
{
_local_val.string_val = 0;
* Copy constructor.
*/
SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
- : _display_name(0),
- _index(node._index),
+ : _index(node._index),
+ _name(node._name),
_parent(0), // don't copy the parent
- _path(0),
_path_cache(0),
_type(node._type),
_tied(node._tied),
_attr(node._attr),
- _count(0),
_listeners(0) // CHECK!!
{
- _name = copy_string(node._name);
_local_val.string_val = 0;
switch (_type) {
case NONE:
SGPropertyNode::SGPropertyNode (const char * name,
int index,
SGPropertyNode * parent)
- : _display_name(0),
- _index(index),
+ : _index(index),
_parent(parent),
- _path(0),
_path_cache(0),
_type(NONE),
_tied(false),
_attr(READ|WRITE),
- _count(0),
_listeners(0)
{
- _name = copy_string(name);
+ int i = 0;
+ _name = parse_name(name, i);
+ if (i != int(strlen(name)) || name[0] == '.')
+ throw string("plain name expected instead of '") + name + '\'';
+
_local_val.string_val = 0;
}
*/
SGPropertyNode::~SGPropertyNode ()
{
- delete [] _name;
- delete [] _display_name;
- delete [] _path;
+ // zero out all parent pointers, else they might be dangling
+ for (unsigned i = 0; i < _children.size(); ++i)
+ _children[i]->_parent = 0;
+ for (unsigned i = 0; i < _removedChildren.size(); ++i)
+ _removedChildren[i]->_parent = 0;
delete _path_cache;
- clear_value();
- delete _listeners;
+ clearValue();
+
+ if (_listeners) {
+ vector<SGPropertyChangeListener*>::iterator it;
+ for (it = _listeners->begin(); it != _listeners->end(); ++it)
+ (*it)->unregister_property(this);
+ delete _listeners;
+ }
}
{
if (target == 0 || _type == ALIAS || _tied)
return false;
- clear_value();
+ clearValue();
_value.alias = target;
_type = ALIAS;
return true;
}
+/**
+ * Remove this node and all children from nodes that link to them
+ * in their path cache.
+ */
+void
+SGPropertyNode::remove_from_path_caches ()
+{
+ for (unsigned int i = 0; i < _children.size(); ++i)
+ _children[i]->remove_from_path_caches();
+
+ for (unsigned int i = 0; i < _linkedNodes.size(); i++)
+ _linkedNodes[i]->erase(this);
+ _linkedNodes.clear();
+}
+
+
+/**
+ * Remove child by position.
+ */
+SGPropertyNode_ptr
+SGPropertyNode::removeChild (int pos, bool keep)
+{
+ SGPropertyNode_ptr node;
+ if (pos < 0 || pos >= (int)_children.size())
+ return node;
+
+ vector<SGPropertyNode_ptr>::iterator it = _children.begin();
+ it += pos;
+ node = _children[pos];
+ _children.erase(it);
+ if (keep) {
+ _removedChildren.push_back(node);
+ }
+
+ node->remove_from_path_caches();
+ node->setAttribute(REMOVED, true);
+ node->clearValue();
+ fireChildRemoved(node);
+ return node;
+}
+
+
/**
* Remove a child node
*/
-SGPropertyNode_ptr
+SGPropertyNode_ptr
SGPropertyNode::removeChild (const char * name, int index, bool keep)
{
SGPropertyNode_ptr ret;
int pos = find_child(name, index, _children);
- if (pos >= 0) {
- vector<SGPropertyNode_ptr>::iterator it = _children.begin();
- it += pos;
- SGPropertyNode_ptr node = _children[pos];
- _children.erase(it);
- if (keep) {
- _removedChildren.push_back(node);
+ if (pos >= 0)
+ ret = removeChild(pos, keep);
+ return ret;
+}
+
+
+/**
+ * Remove all children with the specified name.
+ */
+vector<SGPropertyNode_ptr>
+SGPropertyNode::removeChildren (const char * name, bool keep)
+{
+ vector<SGPropertyNode_ptr> children;
+
+ for (int pos = _children.size() - 1; pos >= 0; pos--)
+ if (compare_strings(_children[pos]->getName(), name))
+ children.push_back(removeChild(pos, keep));
+
+ sort(children.begin(), children.end(), CompareIndices());
+ return children;
+}
+
+
+/**
+ * Remove a linked node.
+ */
+bool
+SGPropertyNode::remove_linked_node (hash_table * node)
+{
+ for (unsigned int i = 0; i < _linkedNodes.size(); i++) {
+ if (_linkedNodes[i] == node) {
+ vector<hash_table *>::iterator it = _linkedNodes.begin();
+ it += i;
+ _linkedNodes.erase(it);
+ return true;
}
- node->setAttribute(REMOVED, true);
- ret = node;
- fireChildRemoved(node);
}
- return ret;
+ return false;
}
const char *
SGPropertyNode::getDisplayName (bool simplify) const
{
- string display = _name;
+ _display_name = _name;
if (_index != 0 || !simplify) {
- char buffer[64];
- sprintf(buffer, "[%d]", _index);
- display += buffer;
+ stringstream sstr;
+ sstr << '[' << _index << ']';
+ _display_name += sstr.str();
}
- _display_name = copy_string(display.c_str());
- return _display_name;
+ return _display_name.c_str();
}
const char *
SGPropertyNode::getPath (bool simplify) const
{
- // Calculate the complete path only once.
- if (_path == 0) {
- string path;
- if (_parent == 0) {
- path = "";
- } else {
- path = _parent->getPath(simplify);
- path += '/';
- path += getDisplayName(simplify);
- }
- _path = copy_string(path.c_str());
+ // Calculate the complete path only once.
+ if (_parent != 0 && _path.empty()) {
+ _path = _parent->getPath(simplify);
+ _path += '/';
+ _path += getDisplayName(simplify);
}
- return _path;
+ return _path.c_str();
}
SGPropertyNode::Type
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
+ clearValue();
_tied = false;
_type = BOOL;
}
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
+ clearValue();
_type = INT;
_local_val.int_val = 0;
}
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
+ clearValue();
_type = LONG;
_local_val.long_val = 0L;
}
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
+ clearValue();
_type = FLOAT;
_local_val.float_val = 0;
}
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
+ clearValue();
_local_val.double_val = value;
_type = DOUBLE;
}
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
+ clearValue();
_type = STRING;
}
bool result = false;
TEST_WRITE;
if (_type == NONE) {
- clear_value();
+ clearValue();
_type = UNSPECIFIED;
}
if (useDefault)
old_val = getBoolValue();
- clear_value();
+ clearValue();
_type = BOOL;
_tied = true;
_value.bool_val = rawValue.clone();
if (useDefault)
old_val = getIntValue();
- clear_value();
+ clearValue();
_type = INT;
_tied = true;
_value.int_val = rawValue.clone();
if (useDefault)
old_val = getLongValue();
- clear_value();
+ clearValue();
_type = LONG;
_tied = true;
_value.long_val = rawValue.clone();
if (useDefault)
old_val = getFloatValue();
- clear_value();
+ clearValue();
_type = FLOAT;
_tied = true;
_value.float_val = rawValue.clone();
if (useDefault)
old_val = getDoubleValue();
- clear_value();
+ clearValue();
_type = DOUBLE;
_tied = true;
_value.double_val = rawValue.clone();
if (useDefault)
old_val = getStringValue();
- clear_value();
+ clearValue();
_type = STRING;
_tied = true;
_value.string_val = rawValue.clone();
switch (_type) {
case BOOL: {
bool val = getBoolValue();
- clear_value();
+ clearValue();
_type = BOOL;
_local_val.bool_val = val;
break;
}
case INT: {
int val = getIntValue();
- clear_value();
+ clearValue();
_type = INT;
_local_val.int_val = val;
break;
}
case LONG: {
long val = getLongValue();
- clear_value();
+ clearValue();
_type = LONG;
_local_val.long_val = val;
break;
}
case FLOAT: {
float val = getFloatValue();
- clear_value();
+ clearValue();
_type = FLOAT;
_local_val.float_val = val;
break;
}
case DOUBLE: {
double val = getDoubleValue();
- clear_value();
+ clearValue();
_type = DOUBLE;
_local_val.double_val = val;
break;
case STRING:
case UNSPECIFIED: {
string val = getStringValue();
- clear_value();
+ clearValue();
_type = STRING;
_local_val.string_val = copy_string(val.c_str());
break;
vector<PathComponent> components;
parse_path(relative_path, components);
if (components.size() > 0)
- components[components.size()-1].index = index;
+ components.back().index = index;
return find_node(this, components, 0, create);
}
}
void
-SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener)
+SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
+ bool initial)
{
if (_listeners == 0)
- _listeners = new vector<SGPropertyChangeListener *>;
+ _listeners = new vector<SGPropertyChangeListener*>;
_listeners->push_back(listener);
listener->register_property(this);
+ if (initial)
+ listener->valueChanged(this);
}
void
SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
{
- vector<SGPropertyChangeListener *>::iterator it =
+ 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;
+ vector<SGPropertyChangeListener*>* tmp = _listeners;
_listeners = 0;
delete tmp;
}
#define HASH_TABLE_SIZE 199
SGPropertyNode::hash_table::entry::entry ()
- : _key(0),
- _value(0)
+ : _value(0)
{
}
{
// Don't delete the value; we don't own
// the pointer.
- delete [] _key;
}
void
SGPropertyNode::hash_table::entry::set_key (const char * key)
{
- _key = copy_string(key);
+ _key = key;
}
void
{
for (int i = 0; i < _length; i++)
delete _entries[i];
+ delete [] _entries;
}
SGPropertyNode::hash_table::entry *
}
}
+bool
+SGPropertyNode::hash_table::bucket::erase (SGPropertyNode * node)
+{
+ for (int i = 0; i < _length; i++) {
+ if (_entries[i]->get_value() == node) {
+ delete _entries[i];
+ for (++i; i < _length; i++) {
+ _entries[i-1] = _entries[i];
+ }
+ _length--;
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+SGPropertyNode::hash_table::bucket::clear (SGPropertyNode::hash_table * owner)
+{
+ for (int i = 0; i < _length; i++) {
+ SGPropertyNode * node = _entries[i]->get_value();
+ if (node)
+ node->remove_linked_node(owner);
+ }
+}
SGPropertyNode::hash_table::hash_table ()
: _data_length(0),
SGPropertyNode::hash_table::~hash_table ()
{
- for (unsigned int i = 0; i < _data_length; i++)
- delete _data[i];
+ for (unsigned int i = 0; i < _data_length; i++) {
+ if (_data[i]) {
+ _data[i]->clear(this);
+ delete _data[i];
+ }
+ }
+ delete [] _data;
}
SGPropertyNode *
}
entry * e = _data[index]->get_entry(key, true);
e->set_value(value);
+ value->add_linked_node(this);
+}
+
+bool
+SGPropertyNode::hash_table::erase (SGPropertyNode * node)
+{
+ for (unsigned int i = 0; i < _data_length; i++)
+ if (_data[i] && _data[i]->erase(node))
+ return true;
+
+ return false;
}
unsigned int
}
-
-/**
- * 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;
-}
-
-
\f
////////////////////////////////////////////////////////////////////////
// Implementation of SGPropertyChangeListener.
SGPropertyChangeListener::~SGPropertyChangeListener ()
{
- // This will come back and remove
- // the current item each time. Is
- // that OK?
- vector<SGPropertyNode *>::iterator it;
- for (it = _properties.begin(); it != _properties.end(); it++)
- (*it)->removeChangeListener(this);
+ for (int i = _properties.size() - 1; i >= 0; i--)
+ _properties[i]->removeChangeListener(this);
}
void