X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fprops%2Fprops.hxx;h=595e013d868585b93d4860c26830a1bef1bf37db;hb=2acac617fcf27b49a9396e9c040c8b4b437529f1;hp=dc7a5878c4c1cf4ecea46b9ac6f7a740efded892;hpb=abd4aa2e6b2c83c1b10eaf24873a06b439dadd24;p=simgear.git diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index dc7a5878..595e013d 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -20,25 +20,61 @@ #include #include #include +#include -#include - -#if PROPS_STANDALONE -#else #include -#include +#if PROPS_STANDALONE +// taken from: boost/utility/enable_if.hpp +#ifndef SG_LOG +# define SG_GENERAL 0 +# define SG_ALERT 0 +# define SG_WARN 1 +# define SG_LOG(type, level, message) (type) ? (std::cerr < + struct enable_if_c { + typedef T type; + }; + + template + struct enable_if_c {}; + template + struct enable_if : public enable_if_c {}; -#include + template + struct disable_if_c { + typedef T type; + }; + + template + struct disable_if_c {}; + + template + struct disable_if : public disable_if_c {}; +} +#else +# include +# include + +# include +# include +# include +#endif #include #include // XXX This whole file should be in the simgear namespace, but I don't // have the guts yet... +using namespace std; + namespace simgear { + + class PropertyInterpolationMgr; + template std::istream& readFrom(std::istream& stream, T& result) { @@ -63,13 +99,6 @@ inline T parseString(const std::string& str) return result; } -// Extended properties -template<> -std::istream& readFrom(std::istream& stream, SGVec3d& result); -template<> -std::istream& readFrom(std::istream& stream, SGVec4d& result); - - /** * Property value types. */ @@ -161,27 +190,14 @@ DEFINTERNALPROP(long, LONG); DEFINTERNALPROP(float, FLOAT); DEFINTERNALPROP(double, DOUBLE); DEFINTERNALPROP(const char *, STRING); +DEFINTERNALPROP(const char[], STRING); #undef DEFINTERNALPROP -template<> -struct PropertyTraits -{ - static const Type type_tag = VEC3D; - enum { Internal = 0 }; -}; - -template<> -struct PropertyTraits -{ - static const Type type_tag = VEC4D; - enum { Internal = 0 }; -}; } } - //////////////////////////////////////////////////////////////////////// // A raw value. // @@ -266,15 +282,14 @@ class SGRawBase : public SGRawExtended /** * Abstract base class for a raw value. * - *

The property manager is implemented in two layers. The {@link - * SGPropertyNode} is the highest and most abstract layer, - * representing an LValue/RValue pair: it records the position of the - * property in the property tree and contains facilities for - * navigation to other nodes. It is guaranteed to be persistent: the - * {@link SGPropertyNode} will not change during a session, even if - * the property is bound and unbound multiple times.

+ * The property manager is implemented in two layers. The SGPropertyNode is the + * highest and most abstract layer, representing an LValue/RValue pair: it + * records the position of the property in the property tree and contains + * facilities for navigation to other nodes. It is guaranteed to be persistent: + * the SGPropertyNode will not change during a session, even if the property is + * bound and unbound multiple times. * - *

When the property value is not managed internally in the + * When the property value is not managed internally in the * SGPropertyNode, the SGPropertyNode will contain a reference to an * SGRawValue (this class), which provides an abstract way to get, * set, and clone the underlying value. The SGRawValue may change @@ -282,13 +297,12 @@ class SGRawBase : public SGRawExtended * unbound to various data source, but the abstract SGPropertyNode * layer insulates the application from those changes. * - *

The SGPropertyNode class always keeps a *copy* of a raw value, - * not the original one passed to it; if you override a derived class - * but do not replace the {@link #clone} method, strange things will - * happen.

+ * The SGPropertyNode class always keeps a *copy* of a raw value, not the + * original one passed to it; if you override a derived class but do not replace + * the {@link SGRaw::clone clone()} method, strange things will happen. * - *

All derived SGRawValue classes must implement {@link #getValue}, - * {@link #setValue}, and {@link #clone} for the appropriate type.

+ * All derived SGRawValue classes must implement getValue(), setValue(), and + * {@link SGRaw::clone clone()} for the appropriate type. * * @see SGPropertyNode * @see SGRawValuePointer @@ -312,7 +326,10 @@ public: * may need different kinds of default values (such as epoch for a * date type). The default value is used when creating new values. */ - static const T DefaultValue; // Default for this kind of raw value. + static T DefaultValue() + { + return T(); + } /** @@ -368,14 +385,15 @@ public: // Default values for every type. //////////////////////////////////////////////////////////////////////// -template<> const bool SGRawValue::DefaultValue; -template<> const int SGRawValue::DefaultValue; -template<> const long SGRawValue::DefaultValue; -template<> const float SGRawValue::DefaultValue; -template<> const double SGRawValue::DefaultValue; -template<> const char * const SGRawValue::DefaultValue; -template<> const SGVec3d SGRawValue::DefaultValue; -template<> const SGVec4d SGRawValue::DefaultValue; +template<> inline bool SGRawValue::DefaultValue() +{ + return false; +} + +template<> inline const char * SGRawValue::DefaultValue() +{ + return ""; +} /** * A raw value bound to a pointer. @@ -486,7 +504,7 @@ public: */ virtual T getValue () const { if (_getter) return (*_getter)(); - else return SGRawValue::DefaultValue; + else return SGRawValue::DefaultValue(); } /** @@ -535,7 +553,7 @@ public: virtual ~SGRawValueFunctionsIndexed () {} virtual T getValue () const { if (_getter) return (*_getter)(_index); - else return SGRawValue::DefaultValue; + else return SGRawValue::DefaultValue(); } virtual bool setValue (T value) { if (_setter) { (*_setter)(_index, value); return true; } @@ -568,7 +586,7 @@ public: virtual ~SGRawValueMethods () {} virtual T getValue () const { if (_getter) { return (_obj.*_getter)(); } - else { return SGRawValue::DefaultValue; } + else { return SGRawValue::DefaultValue(); } } virtual bool setValue (T value) { if (_setter) { (_obj.*_setter)(value); return true; } @@ -602,7 +620,7 @@ public: virtual ~SGRawValueMethodsIndexed () {} virtual T getValue () const { if (_getter) { return (_obj.*_getter)(_index); } - else { return SGRawValue::DefaultValue; } + else { return SGRawValue::DefaultValue(); } } virtual bool setValue (T value) { if (_setter) { (_obj.*_setter)(_index, value); return true; } @@ -684,12 +702,6 @@ std::istream& SGRawBase::readFrom(std::istream& stream) return stream; } -template<> -std::ostream& SGRawBase::printOn(std::ostream& stream) const; -template<> -std::ostream& SGRawBase::printOn(std::ostream& stream) const; - - /** * The smart pointer that manage reference counting */ @@ -702,7 +714,6 @@ namespace simgear typedef std::vector PropertyList; } - /** * The property change listener interface. * @@ -713,9 +724,15 @@ class SGPropertyChangeListener { public: virtual ~SGPropertyChangeListener (); - virtual void valueChanged (SGPropertyNode * node); - virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child); - virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child); + + /// Called if value of \a node has changed. + virtual void valueChanged(SGPropertyNode * node); + + /// Called if \a child has been added to the given \a parent. + virtual void childAdded(SGPropertyNode * parent, SGPropertyNode * child); + + /// Called if \a child has been removed from its \a parent. + virtual void childRemoved(SGPropertyNode * parent, SGPropertyNode * child); protected: friend class SGPropertyNode; @@ -727,7 +744,6 @@ private: }; - /** * A node in a property tree. */ @@ -749,13 +765,17 @@ public: * whether the property should normally be saved and restored.

*/ enum Attribute { + NO_ATTR = 0, READ = 1, WRITE = 2, ARCHIVE = 4, REMOVED = 8, TRACE_READ = 16, TRACE_WRITE = 32, - USERARCHIVE = 64 + USERARCHIVE = 64, + PRESERVE = 128 + // beware: if you add another attribute here, + // also update value of "LAST_USED_ATTRIBUTE". }; @@ -765,7 +785,6 @@ public: */ static const int LAST_USED_ATTRIBUTE; - /** * Default constructor. */ @@ -800,6 +819,11 @@ public: */ const char * getName () const { return _name.c_str(); } + /** + * Get the node's simple name as a string. + */ + const std::string& getNameString () const { return _name; } + /** * Get the node's pretty display name, with subscript when needed. */ @@ -832,7 +856,7 @@ public: /** * Get the number of child nodes. */ - int nChildren () const { return _children.size(); } + int nChildren () const { return (int)_children.size(); } /** @@ -864,24 +888,40 @@ public: } /** - * Create a child node after the last node with the same name. + * Create a new child node with the given name and an unused index + * + * @param min_index Minimal index for new node (skips lower indices) + * @param append Whether to simply use the index after the last used index + * or use a lower, unused index if it exists */ - SGPropertyNode * addChild (const char * name); + SGPropertyNode * addChild ( const char* name, + int min_index = 0, + bool append = true ); + SGPropertyNode * addChild ( const std::string& name, + int min_index = 0, + bool append = true ) + { return addChild(name.c_str(), min_index, append); } /** - * Get a child node by name and index. + * Create multiple child nodes with the given name an unused indices + * + * @param count The number of nodes create + * @param min_index Minimal index for new nodes (skips lower indices) + * @param append Whether to simply use the index after the last used index + * or use a lower, unused index if it exists */ - SGPropertyNode * getChild (const char * name, int index = 0, - bool create = false); + simgear::PropertyList addChildren ( const std::string& name, + size_t count, + int min_index = 0, + bool append = true ); /** * Get a child node by name and index. */ + SGPropertyNode * getChild (const char* name, int index = 0, + bool create = false); SGPropertyNode * getChild (const std::string& name, int index = 0, - bool create = false) - { return getChild(name.c_str(), index, create); } - - + bool create = false); /** * Get a const child node by name and index. */ @@ -905,36 +945,46 @@ public: simgear::PropertyList getChildren (const std::string& name) const { return getChildren(name.c_str()); } + /** + * Remove child by pointer (if it is a child of this node). + * + * @return true, if the node was deleted. + */ + bool removeChild(SGPropertyNode* node); + + // TODO do we need the removeXXX methods to return the deleted nodes? /** * Remove child by position. */ - SGPropertyNode_ptr removeChild (int pos, bool keep = true); + SGPropertyNode_ptr removeChild(int pos); /** * Remove a child node */ - SGPropertyNode_ptr removeChild (const char * name, int index = 0, - bool keep = true); + SGPropertyNode_ptr removeChild(const char * name, int index = 0); /** * Remove a child node */ - SGPropertyNode_ptr removeChild (const std::string& name, int index = 0, - bool keep = true) - { return removeChild(name.c_str(), index, keep); } + SGPropertyNode_ptr removeChild(const std::string& name, int index = 0) + { return removeChild(name.c_str(), index); } /** * Remove all children with the specified name. */ - simgear::PropertyList removeChildren (const char * name, bool keep = true); + simgear::PropertyList removeChildren(const char * name); /** * Remove all children with the specified name. */ - simgear::PropertyList removeChildren (const std::string& name, - bool keep = true) - { return removeChildren(name.c_str(), keep); } + simgear::PropertyList removeChildren(const std::string& name) + { return removeChildren(name.c_str()); } + + /** + * Remove all children (does not change the value of the node) + */ + void removeAllChildren(); // // Alias support. @@ -991,7 +1041,7 @@ public: /** * Get the path to this node from the root. */ - const char * getPath (bool simplify = false) const; + std::string getPath (bool simplify = false) const; /** @@ -1163,6 +1213,18 @@ public: T getValue(typename boost::disable_if_c::Internal> ::type* dummy = 0) const; + /** + * Get a list of values from all children with the given name + */ + template // TODO use C++11 or traits + std::vector getChildValues(const std::string& name) const; + + /** + * Get a list of values from all children with the given name + */ + template + std::vector getChildValues(const std::string& name) const; + /** * Set a bool value for this node. */ @@ -1219,7 +1281,68 @@ public: bool setValue(const T& val, typename boost::disable_if_c::Internal> ::type* dummy = 0); + + template + bool setValue(const char (&val)[N]) + { + return setValue(&val[0]); + } + /** + * Set relative node to given value and afterwards make read only. + * + * @param relative_path Path to node + * @param value Value to set + * + * @return whether value could be set + */ + template + bool setValueReadOnly(const std::string& relative_path, const T& value) + { + SGPropertyNode* node = getNode(relative_path, true); + bool ret = node->setValue(value); + node->setAttributes(READ); + return ret; + } + +#if !PROPS_STANDALONE + /** + * Interpolate current value to target value within given time. + * + * @param type Type of interpolation ("numeric", "color", etc.) + * @param target Node containing target value + * @param duration Duration of interpolation (in seconds) + * @param easing Easing function (http://easings.net/) + */ + bool interpolate( const std::string& type, + const SGPropertyNode& target, + double duration = 0.6, + const std::string& easing = "swing" ); + + /** + * Interpolate current value to a series of values within given durations. + * + * @param type Type of interpolation ("numeric", "color", etc.) + * @param values Nodes containing intermediate and target values + * @param deltas Durations for each interpolation step (in seconds) + * @param easing Easing function (http://easings.net/) + */ + bool interpolate( const std::string& type, + const simgear::PropertyList& values, + const double_list& deltas, + const std::string& easing = "swing" ); + + /** + * Set the interpolation manager used by the interpolate methods. + */ + static void setInterpolationMgr(simgear::PropertyInterpolationMgr* mgr); + + /** + * Get the interpolation manager + */ + static simgear::PropertyInterpolationMgr* getInterpolationMgr(); +#endif + /** * Print the value of the property to a stream. */ @@ -1564,7 +1687,7 @@ public: /** * Get the number of listeners. */ - int nListeners () const { return _listeners ? _listeners->size() : 0; } + int nListeners () const { return _listeners ? (int)_listeners->size() : 0; } /** @@ -1578,18 +1701,47 @@ public: */ void fireChildAdded (SGPropertyNode * child); + /** + * Trigger a child-added and value-changed event for every child (Unlimited + * depth). + * + * @param fire_self Whether to trigger the events also for the node itself. + * + * It can be used to simulating the creation of a property tree, eg. for + * (re)initializing a subsystem which is controlled through the property tree. + */ + void fireCreatedRecursive(bool fire_self = false); /** * Fire a child-removed event to all listeners. */ void fireChildRemoved (SGPropertyNode * child); + /** + * Fire a child-removed event for every child of this node (Unlimited depth) + * + * Upon removal of a child node only for this single node a child-removed + * event is triggered. If eg. resource cleanup relies on receiving a + * child-removed event for every child this method can be used. + */ + void fireChildrenRemovedRecursive(); + /** * Clear any existing value and set the type to NONE. */ void clearValue (); + /** + * Compare two property trees. The property trees are equal if: 1) + * They have no children, and have the same type and the values are + * equal, or 2) have the same number of children, and the + * corresponding children in each tree are equal. "corresponding" + * means have the same name and index. + * + * Attributes, removed children, and aliases aren't considered. + */ + static bool compare (const SGPropertyNode& lhs, const SGPropertyNode& rhs); protected: @@ -1597,15 +1749,20 @@ protected: void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child); void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child); + SGPropertyNode_ptr eraseChild(simgear::PropertyList::iterator child); + /** * Protected constructor for making new nodes on demand. */ - SGPropertyNode (const char * name, int index, SGPropertyNode * parent); + SGPropertyNode (const std::string& name, int index, SGPropertyNode * parent); + template + SGPropertyNode (Itr begin, Itr end, int index, SGPropertyNode * parent); + static simgear::PropertyInterpolationMgr* _interpolation_mgr; private: - // Get the raw value + // Get the raw value bool get_bool () const; int get_int () const; long get_long () const; @@ -1613,7 +1770,7 @@ private: double get_double () const; const char * get_string () const; - // Set the raw value + // Set the raw value bool set_bool (bool value); bool set_int (int value); bool set_long (long value); @@ -1638,31 +1795,18 @@ private: */ void trace_write () const; - - /** - * Remove this node from all nodes that link to it in their path cache. - */ - void remove_from_path_caches(); - - - class hash_table; - int _index; std::string _name; /// To avoid cyclic reference counting loops this shall not be a reference /// counted pointer SGPropertyNode * _parent; simgear::PropertyList _children; - simgear::PropertyList _removedChildren; - std::vector _linkedNodes; - mutable std::string _path; mutable std::string _buffer; - hash_table * _path_cache; simgear::props::Type _type; bool _tied; int _attr; - // The right kind of pointer... + // The right kind of pointer... union { SGPropertyNode * alias; SGRaw* val; @@ -1679,72 +1823,28 @@ private: std::vector * _listeners; - - /** - * Register/unregister node that links to this node in its path cache. - */ - void add_linked_node (hash_table * node) { _linkedNodes.push_back(node); } - bool remove_linked_node (hash_table * node); - - - - /** - * A very simple hash table. - */ - class hash_table { - public: - - /** - * An entry in a bucket in a hash table. - */ - class entry { - public: - entry (); - ~entry (); - const char * get_key () { return _key.c_str(); } - void set_key (const char * key); - SGPropertyNode * get_value () { return _value; } - void set_value (SGPropertyNode * value); - private: - std::string _key; - SGSharedPtr _value; - }; - - - /** - * A bucket in a hash table. - */ - class bucket { - public: - bucket (); - ~bucket (); - entry * get_entry (const char * key, bool create = false); - bool erase (SGPropertyNode * node); - void clear (hash_table * owner); - private: - int _length; - entry ** _entries; - }; - - friend class bucket; - - hash_table (); - ~hash_table (); - SGPropertyNode * get (const char * key); - void put (const char * key, SGPropertyNode * value); - bool erase (SGPropertyNode * node); - - private: - unsigned int hashcode (const char * key); - unsigned int _data_length; - bucket ** _data; - }; - + // Pass name as a pair of iterators + template + SGPropertyNode * getChildImpl (Itr begin, Itr end, int index = 0, bool create = false); + // very internal method + template + SGPropertyNode* getExistingChild (Itr begin, Itr end, int index); + // very internal path parsing function + template + friend SGPropertyNode* find_node_aux(SGPropertyNode * current, SplitItr& itr, + bool create, int last_index); + // For boost + friend size_t hash_value(const SGPropertyNode& node); }; // Convenience functions for use in templates template -T getValue(const SGPropertyNode*); +#if PROPS_STANDALONE +T +#else +typename boost::disable_if, T>::type +#endif +getValue(const SGPropertyNode*); template<> inline bool getValue(const SGPropertyNode* node) { return node->getBoolValue(); } @@ -1773,6 +1873,62 @@ inline const char * getValue(const SGPropertyNode* node) return node->getStringValue (); } +template<> +inline std::string getValue(const SGPropertyNode* node) +{ + return node->getStringValue(); +} + +namespace simgear +{ + /** + * Default trait for extracting enum values from SGPropertyNode. Create your + * own specialization for specific enum types to enable validation of values. + */ + template + struct enum_traits + { + /** + * Typename of the enum + */ + static const char* name() { return typeid(T).name(); } + + /** + * @return Default value (will be used if validation fails) + */ + static T defVal() { return T(); } + + /** + * @return Whether the given integer value has an enum value defined + */ + static bool validate(int) { return true; } + }; +} // namespace simgear + +/** Extract enum from SGPropertyNode */ +template +#if PROPS_STANDALONE +inline T +#else +inline typename boost::enable_if, T>::type +#endif +getValue(const SGPropertyNode* node) +{ + typedef simgear::enum_traits Traits; + int val = node->getIntValue(); + if( !Traits::validate(val) ) + { + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "Invalid value for enum (" << Traits::name() << ", val = " << val << ")" + ); + return Traits::defVal(); + } + return static_cast(node->getIntValue()); +} + inline bool setValue(SGPropertyNode* node, bool value) { return node->setBoolValue(value); @@ -1816,7 +1972,7 @@ bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) return false; useDefault = useDefault && hasValue(); - T old_val = SGRawValue::DefaultValue; + T old_val = SGRawValue::DefaultValue(); if (useDefault) old_val = getValue(this); clearValue(); @@ -1826,8 +1982,12 @@ bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) _type = EXTENDED; _tied = true; _value.val = rawValue.clone(); - if (useDefault) + if (useDefault) { + int save_attributes = getAttributes(); + setAttribute( WRITE, true ); setValue(old_val); + setAttributes( save_attributes ); + } return true; } @@ -1847,7 +2007,7 @@ T SGPropertyNode::getValue(typename boost::disable_if_c::DefaultValue; + return SGRawValue::DefaultValue(); switch (_type) { case EXTENDED: if (_value.val->getType() == PropertyTraits::type_tag) @@ -1857,8 +2017,10 @@ T SGPropertyNode::getValue(typename boost::disable_if_c(make_string()); break; + default: // avoid compiler warning + break; } - return SGRawValue::DefaultValue; + return SGRawValue::DefaultValue(); } template @@ -1868,6 +2030,25 @@ inline T SGPropertyNode::getValue(typename boost::enable_if_c(this); } +template // TODO use C++11 or traits +std::vector SGPropertyNode::getChildValues(const std::string& name) const +{ + const simgear::PropertyList& props = getChildren(name); + std::vector values( props.size() ); + + for( size_t i = 0; i < props.size(); ++i ) + values[i] = props[i]->getValue(); + + return values; +} + +template +inline +std::vector SGPropertyNode::getChildValues(const std::string& name) const +{ + return getChildValues(name); +} + template bool SGPropertyNode::setValue(const T& val, typename boost::disable_if_cgetChild(name, index, true); } + +/** + * Utility function for creation of a child property node using a + * relative path. + */ +namespace simgear +{ +template +inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name) +{ + return parent->getNode(name, true); +} +} + +// For boost::hash +size_t hash_value(const SGPropertyNode& node); + +// Helper comparison and hash functions for common cases + +namespace simgear +{ +namespace props +{ +struct Compare +{ + bool operator()(const SGPropertyNode* lhs, const SGPropertyNode* rhs) const + { + return SGPropertyNode::compare(*lhs, *rhs); + } + bool operator()(SGPropertyNode_ptr lhs, const SGPropertyNode* rhs) const + { + return SGPropertyNode::compare(*lhs, *rhs); + } + bool operator()(const SGPropertyNode* lhs, SGPropertyNode_ptr rhs) const + { + return SGPropertyNode::compare(*lhs, *rhs); + } + bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const + { + return SGPropertyNode::compare(*lhs, *rhs); + } +}; + +struct Hash +{ + size_t operator()(const SGPropertyNode* node) const + { + return hash_value(*node); + } + size_t operator()(SGPropertyNode_ptr node) const + { + return hash_value(*node); + } +}; +} +} + +/** Convenience class for change listener callbacks without + * creating a derived class implementing a "valueChanged" method. + * Also removes listener on destruction automatically. + */ +template +class SGPropertyChangeCallback + : public SGPropertyChangeListener +{ +public: + SGPropertyChangeCallback(T* obj, void (T::*method)(SGPropertyNode*), + SGPropertyNode_ptr property,bool initial=false) + : _obj(obj), _callback(method), _property(property) + { + _property->addChangeListener(this,initial); + } + + SGPropertyChangeCallback(const SGPropertyChangeCallback& other) : + _obj(other._obj), _callback(other._callback), _property(other._property) + { + _property->addChangeListener(this,false); + } + + virtual ~SGPropertyChangeCallback() + { + _property->removeChangeListener(this); + } + void valueChanged (SGPropertyNode * node) + { + (_obj->*_callback)(node); + } +private: + T* _obj; + void (T::*_callback)(SGPropertyNode*); + SGPropertyNode_ptr _property; +}; + #endif // __PROPS_HXX // end of props.hxx