#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);
-
#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++) {
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);
}
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;
clearValue();
- delete _listeners;
+
+ if (_listeners) {
+ vector<SGPropertyChangeListener*>::iterator it;
+ for (it = _listeners->begin(); it != _listeners->end(); ++it)
+ (*it)->unregister_property(this);
+ delete _listeners;
+ }
}
clearValue();
_value.alias = target;
_type = ALIAS;
- _value.alias->incrementRef();
return true;
}
if (_type != ALIAS)
return false;
_type = NONE;
- _value.alias->decrementRef();
_value.alias = 0;
return true;
}
/**
- * Detach a child node from the tree and return a guarded pointer to it.
+ * 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::detachChild (const char * name, int index, bool keep)
+SGPropertyNode_ptr
+SGPropertyNode::removeChild (int pos, 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 (_path_cache)
- _path_cache->erase(name); // EMH - TODO: Take "index" into account!
- node->setAttribute(REMOVED, true);
- node->clearValue();
- ret = node;
- fireChildRemoved(node);
+ 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);
}
- return ret;
+
+ node->remove_from_path_caches();
+ node->setAttribute(REMOVED, true);
+ node->clearValue();
+ fireChildRemoved(node);
+ return node;
}
/**
- * Remove a child node, and all children that aren't referenced.
- * Returns "true" if not all nodes could be deleted.
+ * Remove a child node
*/
-bool
-SGPropertyNode::removeChild (const char * name, int index)
+SGPropertyNode_ptr
+SGPropertyNode::removeChild (const char * name, int index, bool keep)
{
- bool dirty = false;
+ SGPropertyNode_ptr ret;
int pos = find_child(name, index, _children);
- if (pos >= 0) {
- vector<SGPropertyNode_ptr>::iterator it = _children.begin();
- it += pos;
- SGPropertyNode *node = _children[pos];
- if (node->nChildren() && node->removeChildren())
- dirty = true;
-
- if (node->isTied() || node->_count != 1 || node->nChildren())
- dirty = true;
- else {
- if (_path_cache)
- _path_cache->erase(name); // EMH - TODO: Take "index" into account!
-
- node->setAttribute(REMOVED, true);
- node->clearValue();
- fireChildRemoved(node);
- _children.erase(it);
- }
- }
- return dirty;
+ if (pos >= 0)
+ ret = removeChild(pos, keep);
+ return ret;
}
/**
- * Remove all children nodes, or all with a given name. Returns
- * "true" if not all nodes could be deleted.
- */
+ * 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::removeChildren(const char *name)
-{
- bool dirty = false;
- vector<SGPropertyNode_ptr>::iterator it = _children.end();
- vector<SGPropertyNode_ptr>::iterator begin = _children.begin();
- while (it-- != begin) {
- SGPropertyNode *node = *it;
- if (name && !compare_strings(node->getName(), name))
- continue;
-
- if (node->nChildren() && node->removeChildren())
- dirty = true;
-
- if (node->isTied() || node->_count != 1 || node->nChildren())
- dirty = true;
- else {
- if (_path_cache)
- _path_cache->erase(node->getName());
-
- node->setAttribute(REMOVED, true);
- node->clearValue();
- fireChildRemoved(node);
- _children.erase(it);
+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;
}
}
- return dirty;
+ 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
}
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 *
}
}
-void
-SGPropertyNode::hash_table::bucket::erase (const char * key)
+bool
+SGPropertyNode::hash_table::bucket::erase (SGPropertyNode * node)
{
- int i;
- for (i = 0; i < _length; i++) {
- if (!strcmp(_entries[i]->get_key(), key))
- break;
- }
-
- if (i < _length) {
- for (++i; i < _length; i++) {
- _entries[i-1] = _entries[i];
+ 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;
}
- _length--;
}
+ 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);
}
-void
-SGPropertyNode::hash_table::erase (const char * key)
+bool
+SGPropertyNode::hash_table::erase (SGPropertyNode * node)
{
- if (_data_length == 0)
- return;
- unsigned int index = hashcode(key) % _data_length;
- if (_data[index] == 0)
- return;
- _data[index]->erase(key);
+ 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