]> git.mxchange.org Git - simgear.git/blobdiff - simgear/misc/props.cxx
Patch from Cameron Moore:
[simgear.git] / simgear / misc / props.cxx
index 2c2866a89f3d16a720ff05f74eb7615beb86e845..0af19b435799e79f54772a974c9ae617276d069c 100644 (file)
@@ -8,12 +8,18 @@
 
 #include "props.hxx"
 
+#include <algorithm>
+#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
 
@@ -21,12 +27,17 @@ using std::sort;
 #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
+SG_USING_STD(vector<SGPropertyNode_ptr>);
+SG_USING_STD(vector<SGPropertyChangeListener *>);
+SG_USING_STD(vector<SGPropertyNode *>);
 #endif
 
-#include <algorithm>
-#include <stdio.h>
-#include <string.h>
+#endif
 
 
 \f
@@ -40,7 +51,7 @@ SG_USING_STD(sort);
 class CompareIndices
 {
 public:
-  int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const {
+  int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
     return (n1->getIndex() < n2->getIndex());
   }
 };
@@ -215,15 +226,14 @@ parse_path (const string &path, vector<PathComponent> &components)
 ////////////////////////////////////////////////////////////////////////
 
 
-static const char *
+static char *
 copy_string (const char * s)
 {
                                // FIXME: potential buffer overflow.
                                // For some reason, strnlen and
                                // strncpy cause all kinds of crashes.
-  string str = s;
-  char * copy = new char[str.size() + 1];
-  strcpy(copy, str.c_str());
+  char * copy = new char[strlen(s) + 1];
+  strcpy(copy, s);
   return copy;
 }
 
@@ -237,7 +247,7 @@ compare_strings (const char * s1, const char * s2)
  * Locate a child node by name and index.
  */
 static int
-find_child (const char * name, int index, vector<SGPropertyNode *> nodes)
+find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
 {
   int nNodes = nodes.size();
   for (int i = 0; i < nNodes; i++) {
@@ -361,9 +371,15 @@ inline bool
 SGPropertyNode::set_bool (bool val)
 {
   if (_tied) {
-    return _value.bool_val->setValue(val);
+    if (_value.bool_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
   } else {
     _local_val.bool_val = val;
+    fireValueChanged();
     return true;
   }
 }
@@ -372,9 +388,15 @@ inline bool
 SGPropertyNode::set_int (int val)
 {
   if (_tied) {
-    return _value.int_val->setValue(val);
+    if (_value.int_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
   } else {
     _local_val.int_val = val;
+    fireValueChanged();
     return true;
   }
 }
@@ -383,9 +405,15 @@ inline bool
 SGPropertyNode::set_long (long val)
 {
   if (_tied) {
-    return _value.long_val->setValue(val);
+    if (_value.long_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
   } else {
     _local_val.long_val = val;
+    fireValueChanged();
     return true;
   }
 }
@@ -394,9 +422,15 @@ inline bool
 SGPropertyNode::set_float (float val)
 {
   if (_tied) {
-    return _value.float_val->setValue(val);
+    if (_value.float_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
   } else {
     _local_val.float_val = val;
+    fireValueChanged();
     return true;
   }
 }
@@ -405,9 +439,15 @@ inline bool
 SGPropertyNode::set_double (double val)
 {
   if (_tied) {
-    return _value.double_val->setValue(val);
+    if (_value.double_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
   } else {
     _local_val.double_val = val;
+    fireValueChanged();
     return true;
   }
 }
@@ -416,10 +456,16 @@ inline bool
 SGPropertyNode::set_string (const char * val)
 {
   if (_tied) {
-    return _value.string_val->setValue(val);
+    if (_value.string_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
   } else {
-    delete (char *)_local_val.string_val;
+    delete [] _local_val.string_val;
     _local_val.string_val = copy_string(val);
+    fireValueChanged();
     return true;
   }
 }
@@ -474,7 +520,7 @@ SGPropertyNode::clear_value ()
       delete _value.string_val;
       _value.string_val = 0;
     } else {
-      delete (char *)_local_val.string_val;
+      delete [] _local_val.string_val;
     }
     _local_val.string_val = 0;
     break;
@@ -552,23 +598,51 @@ SGPropertyNode::trace_read () const
 #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;
+
 /**
  * Default constructor: always creates a root node.
  */
 SGPropertyNode::SGPropertyNode ()
   : _name(copy_string("")),
+    _display_name(0),
     _index(0),
     _parent(0),
+    _path(0),
     _path_cache(0),
     _type(NONE),
     _tied(false),
-    _attr(READ|WRITE)
+    _attr(READ|WRITE),
+    _count(0),
+    _listeners(0)
 {
   _local_val.string_val = 0;
 }
@@ -578,12 +652,16 @@ SGPropertyNode::SGPropertyNode ()
  * Copy constructor.
  */
 SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
-  : _index(node._index),
+  : _display_name(0),
+    _index(node._index),
     _parent(0),                        // don't copy the parent
+    _path(0),
     _path_cache(0),
     _type(node._type),
     _tied(node._tied),
-    _attr(node._attr)
+    _attr(node._attr),
+    _count(0),
+    _listeners(0)              // CHECK!!
 {
   _name = copy_string(node._name);
   _local_val.string_val = 0;
@@ -659,12 +737,16 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
 SGPropertyNode::SGPropertyNode (const char * name,
                                int index,
                                SGPropertyNode * parent)
-  : _index(index),
+  : _display_name(0),
+    _index(index),
     _parent(parent),
+    _path(0),
     _path_cache(0),
     _type(NONE),
     _tied(false),
-    _attr(READ|WRITE)
+    _attr(READ|WRITE),
+    _count(0),
+    _listeners(0)
 {
   _name = copy_string(name);
   _local_val.string_val = 0;
@@ -676,12 +758,12 @@ SGPropertyNode::SGPropertyNode (const char * name,
  */
 SGPropertyNode::~SGPropertyNode ()
 {
-  delete (char *)_name;
-  for (int i = 0; i < (int)_children.size(); i++) {
-    delete _children[i];
-  }
-//   delete _path_cache;
+  delete [] _name;
+  delete [] _display_name;
+  delete [] _path;
+  delete _path_cache;
   clear_value();
+  delete _listeners;
 }
 
 
@@ -777,8 +859,20 @@ SGPropertyNode::getChild (const char * name, int index, bool create)
   if (pos >= 0) {
     return _children[pos];
   } else if (create) {
-    _children.push_back(new SGPropertyNode(name, index, this));
-    return _children[_children.size()-1];
+    SGPropertyNode_ptr node;
+    pos = find_child(name, index, _removedChildren);
+    if (pos >= 0) {
+      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);
+    fireChildAdded(node);
+    return node;
   } else {
     return 0;
   }
@@ -802,10 +896,10 @@ SGPropertyNode::getChild (const char * name, int index) const
 /**
  * Get all children with the same name (but different indices).
  */
-vector<SGPropertyNode *>
-SGPropertyNode::getChildren (const char * name)
+vector<SGPropertyNode_ptr>
+SGPropertyNode::getChildren (const char * name) const
 {
-  vector<SGPropertyNode *> children;
+  vector<SGPropertyNode_ptr> children;
   int max = _children.size();
 
   for (int i = 0; i < max; i++)
@@ -818,38 +912,60 @@ SGPropertyNode::getChildren (const char * name)
 
 
 /**
- * Get all children const with the same name (but different indices).
+ * Remove a child node
  */
-vector<const SGPropertyNode *>
-SGPropertyNode::getChildren (const char * name) const
+SGPropertyNode_ptr 
+SGPropertyNode::removeChild (const char * name, int index, bool keep)
 {
-  vector<const SGPropertyNode *> children;
-  int max = _children.size();
+  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);
+    }
+    node->setAttribute(REMOVED, true);
+    ret = node;
+    fireChildRemoved(node);
+  }
+  return ret;
+}
 
-  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;
+const char *
+SGPropertyNode::getDisplayName (bool simplify) const
+{
+  string display = _name;
+  if (_index != 0 || !simplify) {
+    char buffer[64];
+    sprintf(buffer, "[%d]", _index);
+    display += buffer;
+  }
+  _display_name = copy_string(display.c_str());
+  return _display_name;
 }
 
 
 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;
+                               // 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());
   }
-  return path.c_str();
+
+  return _path;
 }
 
 SGPropertyNode::Type
@@ -1599,22 +1715,19 @@ SGPropertyNode::getRootNode () const
 SGPropertyNode *
 SGPropertyNode::getNode (const char * relative_path, bool create)
 {
-//   if (_path_cache == 0)
-//     _path_cache = new cache_map;
-
-//   SGPropertyNode * result = (*_path_cache)[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)[relative_path] = result;
-//   }
+  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;
-  vector<PathComponent> components;
-  parse_path(relative_path, components);
-  return find_node(this, components, 0, create);
+  return result;
 }
 
 SGPropertyNode *
@@ -1903,4 +2016,359 @@ SGPropertyNode::untie (const char * relative_path)
   return (node == 0 ? false : node->untie());
 }
 
+void
+SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener)
+{
+  if (_listeners == 0)
+    _listeners = new vector<SGPropertyChangeListener *>;
+  _listeners->push_back(listener);
+  listener->register_property(this);
+}
+
+void
+SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
+{
+  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);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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;
+}
+
+
+\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);
+}
+
+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);
+}
+
+
 // end of props.cxx