X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fprops%2Fprops.cxx;h=09b71fb44c6b91a158fdefd0a86707d65f665cd7;hb=e24e3c06122dcba0d15a3d01be8957bb6b96d739;hp=6730ef132876cf120b86d57d02e569d3724a9069;hpb=a25e859fa773c9463e4bec042b8d0b39041c29ad;p=simgear.git diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index 6730ef13..09b71fb4 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -11,21 +11,25 @@ #endif #include "props.hxx" +#include "vectorPropTemplates.hxx" #include +#include +#include #include #include +#include #include #include #include #include #include +#include +#include #include -#include - #if PROPS_STANDALONE #include #else @@ -91,7 +95,7 @@ public: template inline Range -parse_name (const Range &path) +parse_name (const SGPropertyNode *node, const Range &path) { typename Range::iterator i = path.begin(); typename Range::iterator max = path.end(); @@ -115,15 +119,24 @@ parse_name (const Range &path) } else if (*i == '[' || *i == '/') { break; } else { - throw string("name may contain only ._- and alphanumeric characters"); + string err = "'"; + err.push_back(*i); + err.append("' found in propertyname after '"+node->getNameString()+"'"); + err.append("\nname may contain only ._- and alphanumeric characters"); + throw err; } i++; } } else { - if (path.begin() == i) - throw string("name must begin with alpha or '_'"); + if (path.begin() == i) { + string err = "'"; + err.push_back(*i); + err.append("' found in propertyname after '"+node->getNameString()+"'"); + err.append("\nname must begin with alpha or '_'"); + throw err; + } } return Range(path.begin(), i); } @@ -205,6 +218,26 @@ find_last_child (const char * name, const PropertyList& nodes) return index; } +/** + * Get first unused index for child nodes with the given name + */ +static int +first_unused_index( const char * name, + const PropertyList& nodes, + int min_index ) +{ + const char* nameEnd = name + strlen(name); + + for( int index = min_index; index < std::numeric_limits::max(); ++index ) + { + if( find_child(name, nameEnd, index, nodes) < 0 ) + return index; + } + + SG_LOG(SG_GENERAL, SG_ALERT, "Too much nodes: " << name); + return -1; +} + template inline SGPropertyNode* SGPropertyNode::getExistingChild (Itr begin, Itr end, int index, bool create) @@ -265,7 +298,7 @@ find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create, // Empty name at this point is empty, not root. if (token.empty()) return find_node_aux(current, ++itr, create, last_index); - Range name = parse_name(token); + Range name = parse_name(current, token); if (equals(name, ".")) return find_node_aux(current, ++itr, create, last_index); if (equals(name, "..")) { @@ -630,7 +663,7 @@ SGPropertyNode::trace_read () const * Last used attribute * Update as needed when enum Attribute is changed */ -const int SGPropertyNode::LAST_USED_ATTRIBUTE = USERARCHIVE; +const int SGPropertyNode::LAST_USED_ATTRIBUTE = PRESERVE; /** * Default constructor: always creates a root node. @@ -638,7 +671,6 @@ const int SGPropertyNode::LAST_USED_ATTRIBUTE = USERARCHIVE; SGPropertyNode::SGPropertyNode () : _index(0), _parent(0), - _path_cache(0), _type(props::NONE), _tied(false), _attr(READ|WRITE), @@ -653,10 +685,10 @@ SGPropertyNode::SGPropertyNode () * Copy constructor. */ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) - : _index(node._index), + : SGReferenced(node), + _index(node._index), _name(node._name), _parent(0), // don't copy the parent - _path_cache(0), _type(node._type), _tied(node._tied), _attr(node._attr), @@ -712,7 +744,6 @@ SGPropertyNode::SGPropertyNode (Itr begin, Itr end, : _index(index), _name(begin, end), _parent(parent), - _path_cache(0), _type(props::NONE), _tied(false), _attr(READ|WRITE), @@ -730,7 +761,6 @@ SGPropertyNode::SGPropertyNode (const string& name, : _index(index), _name(name), _parent(parent), - _path_cache(0), _type(props::NONE), _tied(false), _attr(READ|WRITE), @@ -752,7 +782,6 @@ SGPropertyNode::~SGPropertyNode () _children[i]->_parent = 0; for (unsigned i = 0; i < _removedChildren.size(); ++i) _removedChildren[i]->_parent = 0; - delete _path_cache; clearValue(); if (_listeners) { @@ -770,13 +799,41 @@ SGPropertyNode::~SGPropertyNode () bool SGPropertyNode::alias (SGPropertyNode * target) { - if (target == 0 || _type == props::ALIAS || _tied) - return false; - clearValue(); - get(target); - _value.alias = target; - _type = props::ALIAS; - return true; + if (target && (_type != props::ALIAS) && (!_tied)) + { + clearValue(); + get(target); + _value.alias = target; + _type = props::ALIAS; + return true; + } + +#if PROPS_STANDALONE +#else + if (!target) + { + SG_LOG(SG_GENERAL, SG_ALERT, + "Failed to create alias for " << getPath() << ". " + "The target property does not exist."); + } + else + if (_type == props::ALIAS) + { + if (_value.alias == target) + return true; // ok, identical alias requested + SG_LOG(SG_GENERAL, SG_ALERT, + "Failed to create alias at " << target->getPath() << ". " + "Source "<< getPath() << " is already aliasing another property."); + } + else + if (_tied) + { + SG_LOG(SG_GENERAL, SG_ALERT, "Failed to create alias at " << target->getPath() << ". " + "Source " << getPath() << " is a tied property."); + } +#endif + + return false; } @@ -823,9 +880,11 @@ SGPropertyNode::getAliasTarget () const * create a non-const child by name after the last node with the same name. */ SGPropertyNode * -SGPropertyNode::addChild (const char * name) +SGPropertyNode::addChild(const char * name, int min_index, bool append) { - int pos = find_last_child(name, _children)+1; + int pos = append + ? std::max(find_last_child(name, _children) + 1, min_index) + : first_unused_index(name, _children, min_index); SGPropertyNode_ptr node; node = new SGPropertyNode(name, name + strlen(name), pos, this); @@ -834,6 +893,52 @@ SGPropertyNode::addChild (const char * name) return node; } +/** + * Create multiple children with unused indices + */ +simgear::PropertyList +SGPropertyNode::addChildren( const std::string& name, + size_t count, + int min_index, + bool append ) +{ + simgear::PropertyList nodes; + std::set used_indices; + + if( !append ) + { + // First grab all used indices. This saves us of testing every index if it + // is used for every element to be created + for( size_t i = 0; i < nodes.size(); i++ ) + { + const SGPropertyNode* node = nodes[i]; + + if( node->getNameString() == name && node->getIndex() >= min_index ) + used_indices.insert(node->getIndex()); + } + } + else + { + // If we don't want to fill the holes just find last node + min_index = std::max(find_last_child(name.c_str(), _children) + 1, min_index); + } + + for( int index = min_index; + index < std::numeric_limits::max() && nodes.size() < count; + ++index ) + { + if( used_indices.find(index) == used_indices.end() ) + { + SGPropertyNode_ptr node; + node = new SGPropertyNode(name, index, this); + _children.push_back(node); + fireChildAdded(node); + nodes.push_back(node); + } + } + + return nodes; +} /** * Get a non-const child by index. @@ -920,22 +1025,6 @@ SGPropertyNode::getChildren (const char * name) const } -/** - * 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. */ @@ -954,7 +1043,6 @@ SGPropertyNode::removeChild (int pos, bool keep) _removedChildren.push_back(node); } - node->remove_from_path_caches(); node->setAttribute(REMOVED, true); node->clearValue(); fireChildRemoved(node); @@ -992,25 +1080,6 @@ SGPropertyNode::removeChildren (const char * name, bool keep) 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::iterator it = _linkedNodes.begin(); - it += i; - _linkedNodes.erase(it); - return true; - } - } - return false; -} - - string SGPropertyNode::getDisplayName (bool simplify) const { @@ -1023,18 +1092,22 @@ SGPropertyNode::getDisplayName (bool simplify) const return display_name; } - -const char * +string SGPropertyNode::getPath (bool simplify) const { - // Calculate the complete path only once. - if (_parent != 0 && _path.empty()) { - _path = _parent->getPath(simplify); - _path += '/'; - _path += getDisplayName(simplify); + typedef std::vector PList; + PList pathList; + for (const SGPropertyNode* node = this; node->_parent; node = node->_parent) + pathList.push_back(node); + string result; + for (PList::reverse_iterator itr = pathList.rbegin(), + rend = pathList.rend(); + itr != rend; + ++itr) { + result += '/'; + result += (*itr)->getDisplayName(simplify); } - - return _path.c_str(); + return result; } props::Type @@ -1640,8 +1713,12 @@ bool SGPropertyNode::tie (const SGRawValue &rawValue, _tied = true; _value.val = rawValue.clone(); - if (useDefault) + if (useDefault) { + int save_attributes = getAttributes(); + setAttribute( WRITE, true ); setStringValue(old_val.c_str()); + setAttributes( save_attributes ); + } return true; } @@ -1735,20 +1812,10 @@ SGPropertyNode * SGPropertyNode::getNode (const char * relative_path, bool create) { using namespace boost; - if (_path_cache == 0) - _path_cache = new hash_table; - - SGPropertyNode * result = _path_cache->get(relative_path); - if (result == 0) { - result = find_node(this, - make_iterator_range(relative_path, relative_path - + strlen(relative_path)), - create); - if (result != 0) - _path_cache->put(relative_path, result); - } - return result; + return find_node(this, make_iterator_range(relative_path, relative_path + + strlen(relative_path)), + create); } SGPropertyNode * @@ -2051,6 +2118,8 @@ SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener, void SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener) { + if (_listeners == 0) + return; vector::iterator it = find(_listeners->begin(), _listeners->end(), listener); if (it != _listeners->end()) { @@ -2120,172 +2189,6 @@ SGPropertyNode::fireChildRemoved (SGPropertyNode * parent, _parent->fireChildRemoved(parent, child); } - - -//////////////////////////////////////////////////////////////////////// -// Simplified hash table for caching paths. -//////////////////////////////////////////////////////////////////////// - -#define HASH_TABLE_SIZE 199 - -SGPropertyNode::hash_table::entry::entry () - : _value(0) -{ -} - -SGPropertyNode::hash_table::entry::~entry () -{ - // Don't delete the value; we don't own - // the pointer. -} - -void -SGPropertyNode::hash_table::entry::set_key (const char * key) -{ - _key = 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]; - delete [] _entries; -} - -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; - } -} - -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), - _data(0) -{ -} - -SGPropertyNode::hash_table::~hash_table () -{ - for (unsigned int i = 0; i < _data_length; i++) { - if (_data[i]) { - _data[i]->clear(this); - delete _data[i]; - } - } - delete [] _data; -} - -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); - 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 -SGPropertyNode::hash_table::hashcode (const char * key) -{ - unsigned int hash = 0; - while (*key != 0) { - hash = 31 * hash + *key; - key++; - } - return hash; -} - - //////////////////////////////////////////////////////////////////////// // Implementation of SGPropertyChangeListener. @@ -2379,6 +2282,146 @@ std::istream& readFrom(std::istream& stream, SGVec4d& result) } return stream; } + +namespace +{ +bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) +{ + props::Type ltype = lhs.getType(); + props::Type rtype = rhs.getType(); + if (ltype != rtype) + return false; + switch (ltype) { + case props::NONE: + return true; + case props::ALIAS: + return false; // XXX Should we look in aliases? + case props::BOOL: + return lhs.getValue() == rhs.getValue(); + case props::INT: + return lhs.getValue() == rhs.getValue(); + case props::LONG: + return lhs.getValue() == rhs.getValue(); + case props::FLOAT: + return lhs.getValue() == rhs.getValue(); + case props::DOUBLE: + return lhs.getValue() == rhs.getValue(); + case props::STRING: + case props::UNSPECIFIED: + return !strcmp(lhs.getStringValue(), rhs.getStringValue()); + case props::VEC3D: + return lhs.getValue() == rhs.getValue(); + case props::VEC4D: + return lhs.getValue() == rhs.getValue(); + default: + return false; + } +} +} +} + +bool SGPropertyNode::compare(const SGPropertyNode& lhs, + const SGPropertyNode& rhs) +{ + if (&lhs == &rhs) + return true; + int lhsChildren = lhs.nChildren(); + int rhsChildren = rhs.nChildren(); + if (lhsChildren != rhsChildren) + return false; + if (lhsChildren == 0) + return compareNodeValue(lhs, rhs); + for (size_t i = 0; i < lhs._children.size(); ++i) { + const SGPropertyNode* lchild = lhs._children[i]; + const SGPropertyNode* rchild = rhs._children[i]; + // I'm guessing that the nodes will usually be in the same + // order. + if (lchild->getIndex() != rchild->getIndex() + || lchild->getNameString() != rchild->getNameString()) { + rchild = 0; + for (PropertyList::const_iterator itr = rhs._children.begin(), + end = rhs._children.end(); + itr != end; + ++itr) + if (lchild->getIndex() == (*itr)->getIndex() + && lchild->getNameString() == (*itr)->getNameString()) { + rchild = *itr; + break; + } + if (!rchild) + return false; + } + if (!compare(*lchild, *rchild)) + return false; + } + return true; +} + +struct PropertyPlaceLess { + typedef bool result_type; + bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const + { + int comp = lhs->getNameString().compare(rhs->getNameString()); + if (comp == 0) + return lhs->getIndex() < rhs->getIndex(); + else + return comp < 0; + } +}; + +size_t hash_value(const SGPropertyNode& node) +{ + using namespace boost; + + if (node.nChildren() == 0) { + switch (node.getType()) { + case props::NONE: + return 0; + + case props::BOOL: + return hash_value(node.getValue()); + case props::INT: + return hash_value(node.getValue()); + case props::LONG: + return hash_value(node.getValue()); + case props::FLOAT: + return hash_value(node.getValue()); + case props::DOUBLE: + return hash_value(node.getValue()); + case props::STRING: + case props::UNSPECIFIED: + { + const char *val = node.getStringValue(); + return hash_range(val, val + strlen(val)); + } + case props::VEC3D: + { + const SGVec3d val = node.getValue(); + return hash_range(&val[0], &val[3]); + } + case props::VEC4D: + { + const SGVec4d val = node.getValue(); + return hash_range(&val[0], &val[4]); + } + case props::ALIAS: // XXX Should we look in aliases? + default: + return 0; + } + } else { + size_t seed = 0; + PropertyList children(node._children.begin(), node._children.end()); + sort(children.begin(), children.end(), PropertyPlaceLess()); + for (PropertyList::const_iterator itr = children.begin(), + end = children.end(); + itr != end; + ++itr) { + hash_combine(seed, (*itr)->_name); + hash_combine(seed, (*itr)->_index); + hash_combine(seed, hash_value(**itr)); + } + return seed; + } } // end of props.cxx