From: curt Date: Tue, 6 May 2003 23:40:43 +0000 (+0000) Subject: - Moved some property specific code into simgear/props/ X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=525d2df3ccbf80152492a8632267f228a41ac393;p=simgear.git - Moved some property specific code into simgear/props/ - Split out the condition code from fgfs/src/Main/fg_props and put it in it's own source file in simgear/props/ - Created a scene subdirectory for scenery, model, and material property related code. - Moved location.[ch]xx into simgear/scene/model/ - The location and condition code had dependencies on flightgear's global state (all the globals-> stuff, the flightgear property tree, etc.) SimGear code can't depend on it so that data has to be passed as parameters to the functions/methods/constructors. --- diff --git a/configure.ac b/configure.ac index 7d0e5354..eeab41a4 100644 --- a/configure.ac +++ b/configure.ac @@ -415,7 +415,10 @@ AC_CONFIG_FILES([ \ simgear/math/Makefile \ simgear/metar/Makefile \ simgear/misc/Makefile \ + simgear/props/Makefile \ simgear/route/Makefile \ + simgear/scene/Makefile \ + simgear/scene/model/Makefile \ simgear/screen/Makefile \ simgear/serial/Makefile \ simgear/sky/Makefile \ diff --git a/simgear/Makefile.am b/simgear/Makefile.am index 8a2c435c..ccdcf2bf 100644 --- a/simgear/Makefile.am +++ b/simgear/Makefile.am @@ -23,7 +23,9 @@ SUBDIRS = \ magvar \ math \ $(METAR_DIRS) \ + props \ route \ + scene \ screen \ serial \ sky \ diff --git a/simgear/misc/Makefile.am b/simgear/misc/Makefile.am index 6f4e3828..8c6d70da 100644 --- a/simgear/misc/Makefile.am +++ b/simgear/misc/Makefile.am @@ -5,8 +5,6 @@ lib_LIBRARIES = libsgmisc.a include_HEADERS = \ commands.hxx \ exception.hxx \ - props.hxx \ - props_io.hxx \ sg_path.hxx \ sgstream.hxx \ stopwatch.hxx \ @@ -18,8 +16,6 @@ include_HEADERS = \ libsgmisc_a_SOURCES = \ commands.cxx \ exception.cxx \ - props.cxx \ - props_io.cxx \ sg_path.cxx \ sgstream.cxx \ strutils.cxx \ @@ -27,12 +23,12 @@ libsgmisc_a_SOURCES = \ texcoord.cxx \ zfstream.cxx -noinst_PROGRAMS = props_test tabbed_value_test - -props_test_SOURCES = props_test.cxx -props_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a +noinst_PROGRAMS = tabbed_value_test tabbed_value_test_SOURCES = tabbed_values_test.cxx -tabbed_value_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a +tabbed_value_test_LDADD = \ + $(top_builddir)/simgear/misc/libsgmisc.a \ + $(top_builddir)/simgear/xml/libsgxml.a \ + $(top_builddir)/simgear/debug/libsgdebug.a INCLUDES = -I$(top_srcdir) diff --git a/simgear/misc/commands.cxx b/simgear/misc/commands.cxx index 88869848..2e2c5921 100644 --- a/simgear/misc/commands.cxx +++ b/simgear/misc/commands.cxx @@ -4,8 +4,9 @@ // // $Id$ +#include + #include "commands.hxx" -#include "props_io.hxx" diff --git a/simgear/misc/commands.hxx b/simgear/misc/commands.hxx index 55f193b4..4ab28a09 100644 --- a/simgear/misc/commands.hxx +++ b/simgear/misc/commands.hxx @@ -17,7 +17,7 @@ #include #include -#include "props.hxx" +#include SG_USING_STD(string); SG_USING_STD(map); diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx deleted file mode 100644 index 0af19b43..00000000 --- a/simgear/misc/props.cxx +++ /dev/null @@ -1,2374 +0,0 @@ -// props.cxx - implementation of a property list. -// Started Fall 2000 by David Megginson, david@megginson.com -// This code is released into the Public Domain. -// -// See props.html for documentation [replace with URL when available]. -// -// $Id$ - -#include "props.hxx" - -#include -#include -#include - -#if PROPS_STANDALONE - -#include -using std::cerr; -using std::endl; -using std::find; -using std::sort; -using std::vector; - -#else - -#include -#include - -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); -SG_USING_STD(vector); -SG_USING_STD(vector); -#endif - -#endif - - - -//////////////////////////////////////////////////////////////////////// -// Local classes. -//////////////////////////////////////////////////////////////////////// - -/** - * Comparator class for sorting by index. - */ -class CompareIndices -{ -public: - int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const { - return (n1->getIndex() < n2->getIndex()); - } -}; - - - -//////////////////////////////////////////////////////////////////////// -// Convenience macros for value access. -//////////////////////////////////////////////////////////////////////// - -#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt -#define TEST_WRITE if (!getAttribute(WRITE)) return false - - - -//////////////////////////////////////////////////////////////////////// -// Default values for every type. -//////////////////////////////////////////////////////////////////////// - -const bool SGRawValue::DefaultValue = false; -const int SGRawValue::DefaultValue = 0; -const long SGRawValue::DefaultValue = 0L; -const float SGRawValue::DefaultValue = 0.0; -const double SGRawValue::DefaultValue = 0.0L; -const char * const SGRawValue::DefaultValue = ""; - - - -//////////////////////////////////////////////////////////////////////// -// Local path normalization code. -//////////////////////////////////////////////////////////////////////// - -/** - * A component in a path. - */ -struct PathComponent -{ - string name; - int index; -}; - -/** - * Parse the name for a path component. - * - * Name: [_a-zA-Z][-._a-zA-Z0-9]* - */ -static inline const string -parse_name (const string &path, int &i) -{ - string name = ""; - int max = path.size(); - - if (path[i] == '.') { - i++; - if (i < max && path[i] == '.') { - i++; - name = ".."; - } else { - name = "."; - } - if (i < max && path[i] != '/') - throw string(string("Illegal character after ") + name); - } - - else if (isalpha(path[i]) || path[i] == '_') { - name += path[i]; - i++; - - // The rules inside a name are a little - // less restrictive. - while (i < max) { - if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' || - path[i] == '-' || path[i] == '.') { - name += path[i]; - } else if (path[i] == '[' || path[i] == '/') { - break; - } else { - throw string("name may contain only ._- and alphanumeric characters"); - } - i++; - } - } - - else { - if (name.size() == 0) - throw string("name must begin with alpha or '_'"); - } - - return name; -} - - -/** - * Parse the optional integer index for a path component. - * - * Index: "[" [0-9]+ "]" - */ -static inline int -parse_index (const string &path, int &i) -{ - int index = 0; - - if (path[i] != '[') - return 0; - else - i++; - - for (int max = path.size(); i < max; i++) { - if (isdigit(path[i])) { - index = (index * 10) + (path[i] - '0'); - } else if (path[i] == ']') { - i++; - return index; - } else { - break; - } - } - - throw string("unterminated index (looking for ']')"); -} - - -/** - * Parse a single path component. - * - * Component: Name Index? - */ -static inline PathComponent -parse_component (const string &path, int &i) -{ - PathComponent component; - component.name = parse_name(path, i); - if (component.name[0] != '.') - component.index = parse_index(path, i); - else - component.index = -1; - return component; -} - - -/** - * Parse a path into its components. - */ -static void -parse_path (const string &path, vector &components) -{ - int pos = 0; - int max = path.size(); - - // Check for initial '/' - if (path[pos] == '/') { - PathComponent root; - root.name = ""; - root.index = -1; - components.push_back(root); - pos++; - while (pos < max && path[pos] == '/') - pos++; - } - - while (pos < max) { - components.push_back(parse_component(path, pos)); - while (pos < max && path[pos] == '/') - pos++; - } -} - - - -//////////////////////////////////////////////////////////////////////// -// Other static utility functions. -//////////////////////////////////////////////////////////////////////// - - -static char * -copy_string (const char * s) -{ - // FIXME: potential buffer overflow. - // For some reason, strnlen and - // strncpy cause all kinds of crashes. - char * copy = new char[strlen(s) + 1]; - strcpy(copy, s); - return copy; -} - -static bool -compare_strings (const char * s1, const char * s2) -{ - return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN); -} - -/** - * Locate a child node by name and index. - */ -static int -find_child (const char * name, int index, vector nodes) -{ - int nNodes = nodes.size(); - for (int i = 0; i < nNodes; i++) { - SGPropertyNode * node = nodes[i]; - if (compare_strings(node->getName(), name) && node->getIndex() == index) - return i; - } - return -1; -} - - -/** - * Locate another node, given a relative path. - */ -static SGPropertyNode * -find_node (SGPropertyNode * current, - const vector &components, - int position, - bool create) -{ - // Run off the end of the list - if (current == 0) { - return 0; - } - - // Success! This is the one we want. - else if (position >= (int)components.size()) { - return current; - } - - // Empty component means root. - else if (components[position].name == "") { - return find_node(current->getRootNode(), components, position + 1, create); - } - - // . means current directory - else if (components[position].name == ".") { - return find_node(current, components, position + 1, create); - } - - // .. means parent directory - else if (components[position].name == "..") { - SGPropertyNode * parent = current->getParent(); - if (parent == 0) - throw string("Attempt to move past root with '..'"); - else - return find_node(parent, components, position + 1, create); - } - - // Otherwise, a child name - else { - SGPropertyNode * child = - current->getChild(components[position].name.c_str(), - components[position].index, - create); - return find_node(child, components, position + 1, create); - } -} - - - -//////////////////////////////////////////////////////////////////////// -// Private methods from SGPropertyNode (may be inlined for speed). -//////////////////////////////////////////////////////////////////////// - -inline bool -SGPropertyNode::get_bool () const -{ - if (_tied) - return _value.bool_val->getValue(); - else - return _local_val.bool_val; -} - -inline int -SGPropertyNode::get_int () const -{ - if (_tied) - return _value.int_val->getValue(); - else - return _local_val.int_val; -} - -inline long -SGPropertyNode::get_long () const -{ - if (_tied) - return _value.long_val->getValue(); - else - return _local_val.long_val; -} - -inline float -SGPropertyNode::get_float () const -{ - if (_tied) - return _value.float_val->getValue(); - else - return _local_val.float_val; -} - -inline double -SGPropertyNode::get_double () const -{ - if (_tied) - return _value.double_val->getValue(); - else - return _local_val.double_val; -} - -inline const char * -SGPropertyNode::get_string () const -{ - if (_tied) - return _value.string_val->getValue(); - else - return _local_val.string_val; -} - -inline bool -SGPropertyNode::set_bool (bool val) -{ - if (_tied) { - if (_value.bool_val->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.bool_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_int (int val) -{ - if (_tied) { - if (_value.int_val->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.int_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_long (long val) -{ - if (_tied) { - if (_value.long_val->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.long_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_float (float val) -{ - if (_tied) { - if (_value.float_val->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.float_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_double (double val) -{ - if (_tied) { - if (_value.double_val->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.double_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_string (const char * val) -{ - if (_tied) { - if (_value.string_val->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - delete [] _local_val.string_val; - _local_val.string_val = copy_string(val); - fireValueChanged(); - return true; - } -} - -void -SGPropertyNode::clear_value () -{ - switch (_type) { - case NONE: - break; - case ALIAS: - _value.alias = 0; - break; - case BOOL: - if (_tied) { - delete _value.bool_val; - _value.bool_val = 0; - } - _local_val.bool_val = SGRawValue::DefaultValue; - break; - case INT: - if (_tied) { - delete _value.int_val; - _value.int_val = 0; - } - _local_val.int_val = SGRawValue::DefaultValue; - break; - case LONG: - if (_tied) { - delete _value.long_val; - _value.long_val = 0L; - } - _local_val.long_val = SGRawValue::DefaultValue; - break; - case FLOAT: - if (_tied) { - delete _value.float_val; - _value.float_val = 0; - } - _local_val.float_val = SGRawValue::DefaultValue; - break; - case DOUBLE: - if (_tied) { - delete _value.double_val; - _value.double_val = 0; - } - _local_val.double_val = SGRawValue::DefaultValue; - break; - case STRING: - case UNSPECIFIED: - if (_tied) { - delete _value.string_val; - _value.string_val = 0; - } else { - delete [] _local_val.string_val; - } - _local_val.string_val = 0; - break; - } - _tied = false; - _type = NONE; -} - - -/** - * Get the value as a string. - */ -const char * -SGPropertyNode::make_string () const -{ - if (!getAttribute(READ)) - return ""; - - switch (_type) { - case ALIAS: - return _value.alias->getStringValue(); - case BOOL: - if (get_bool()) - return "true"; - else - return "false"; - case INT: - sprintf(_buffer, "%d", get_int()); - return _buffer; - case LONG: - sprintf(_buffer, "%ld", get_long()); - return _buffer; - case FLOAT: - sprintf(_buffer, "%f", get_float()); - return _buffer; - case DOUBLE: - sprintf(_buffer, "%f", get_double()); - return _buffer; - case STRING: - case UNSPECIFIED: - return get_string(); - case NONE: - default: - return ""; - } -} - -/** - * Trace a write access for a property. - */ -void -SGPropertyNode::trace_write () const -{ -#if PROPS_STANDALONE - cerr << "TRACE: Write node " << getPath () << ", value\"" - << make_string() << '"' << endl; -#else - SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath() - << ", value\"" << make_string() << '"'); -#endif -} - -/** - * Trace a read access for a property. - */ -void -SGPropertyNode::trace_read () const -{ -#if PROPS_STANDALONE - cerr << "TRACE: Write node " << getPath () << ", value \"" - << make_string() << '"' << endl; -#else - SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath() - << ", value \"" << make_string() << '"'); -#endif -} - -/** - * Increment reference counter - */ -void -SGPropertyNode::incrementRef() -{ - ++_count; -} - -/** - * Decrement reference counter - */ -int -SGPropertyNode::decrementRef() -{ - return --_count; -} - - - -//////////////////////////////////////////////////////////////////////// -// 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), - _count(0), - _listeners(0) -{ - _local_val.string_val = 0; -} - - -/** - * Copy constructor. - */ -SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) - : _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), - _count(0), - _listeners(0) // CHECK!! -{ - _name = copy_string(node._name); - _local_val.string_val = 0; - switch (_type) { - case NONE: - break; - case ALIAS: - _value.alias = node._value.alias; - _tied = false; - break; - case BOOL: - if (_tied) { - _tied = true; - _value.bool_val = node._value.bool_val->clone(); - } else { - _tied = false; - set_bool(node.get_bool()); - } - break; - case INT: - if (_tied) { - _tied = true; - _value.int_val = node._value.int_val->clone(); - } else { - _tied = false; - set_int(node.get_int()); - } - break; - case LONG: - if (_tied) { - _tied = true; - _value.long_val = node._value.long_val->clone(); - } else { - _tied = false; - set_long(node.get_long()); - } - break; - case FLOAT: - if (_tied) { - _tied = true; - _value.float_val = node._value.float_val->clone(); - } else { - _tied = false; - set_float(node.get_float()); - } - break; - case DOUBLE: - if (_tied) { - _tied = true; - _value.double_val = node._value.double_val->clone(); - } else { - _tied = false; - set_double(node.get_double()); - } - break; - case STRING: - case UNSPECIFIED: - if (_tied) { - _tied = true; - _value.string_val = node._value.string_val->clone(); - } else { - _tied = false; - set_string(node.get_string()); - } - break; - } -} - - -/** - * Convenience constructor. - */ -SGPropertyNode::SGPropertyNode (const char * name, - int index, - SGPropertyNode * parent) - : _display_name(0), - _index(index), - _parent(parent), - _path(0), - _path_cache(0), - _type(NONE), - _tied(false), - _attr(READ|WRITE), - _count(0), - _listeners(0) -{ - _name = copy_string(name); - _local_val.string_val = 0; -} - - -/** - * Destructor. - */ -SGPropertyNode::~SGPropertyNode () -{ - delete [] _name; - delete [] _display_name; - delete [] _path; - delete _path_cache; - clear_value(); - delete _listeners; -} - - -/** - * Alias to another node. - */ -bool -SGPropertyNode::alias (SGPropertyNode * target) -{ - if (target == 0 || _type == ALIAS || _tied) - return false; - clear_value(); - _value.alias = target; - _type = ALIAS; - return true; -} - - -/** - * Alias to another node by path. - */ -bool -SGPropertyNode::alias (const char * path) -{ - return alias(getNode(path, true)); -} - - -/** - * Remove an alias. - */ -bool -SGPropertyNode::unalias () -{ - if (_type != ALIAS) - return false; - _type = NONE; - _value.alias = 0; - return true; -} - - -/** - * Get the target of an alias. - */ -SGPropertyNode * -SGPropertyNode::getAliasTarget () -{ - return (_type == ALIAS ? _value.alias : 0); -} - - -const SGPropertyNode * -SGPropertyNode::getAliasTarget () const -{ - return (_type == ALIAS ? _value.alias : 0); -} - - -/** - * Get a non-const child by index. - */ -SGPropertyNode * -SGPropertyNode::getChild (int position) -{ - if (position >= 0 && position < nChildren()) - return _children[position]; - else - return 0; -} - - -/** - * Get a const child by index. - */ -const SGPropertyNode * -SGPropertyNode::getChild (int position) const -{ - if (position >= 0 && position < nChildren()) - return _children[position]; - else - return 0; -} - - -/** - * Get a non-const child by name and index, creating if necessary. - */ -SGPropertyNode * -SGPropertyNode::getChild (const char * name, int index, bool create) -{ - int pos = find_child(name, index, _children); - if (pos >= 0) { - return _children[pos]; - } else if (create) { - SGPropertyNode_ptr node; - pos = find_child(name, index, _removedChildren); - if (pos >= 0) { - vector::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; - } -} - - -/** - * Get a const child by name and index. - */ -const SGPropertyNode * -SGPropertyNode::getChild (const char * name, int index) const -{ - int pos = find_child(name, index, _children); - if (pos >= 0) - return _children[pos]; - else - return 0; -} - - -/** - * Get all children with the same name (but different indices). - */ -vector -SGPropertyNode::getChildren (const char * name) const -{ - vector children; - int max = _children.size(); - - 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; -} - - -/** - * Remove a child node - */ -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::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; -} - - -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 -{ - // 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; -} - -SGPropertyNode::Type -SGPropertyNode::getType () const -{ - if (_type == ALIAS) - return _value.alias->getType(); - else - return _type; -} - - -bool -SGPropertyNode::getBoolValue () const -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == BOOL) - return get_bool(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue; - switch (_type) { - case ALIAS: - return _value.alias->getBoolValue(); - case BOOL: - return get_bool(); - case INT: - return get_int() == 0 ? false : true; - case LONG: - return get_long() == 0L ? false : true; - case FLOAT: - return get_float() == 0.0 ? false : true; - case DOUBLE: - return get_double() == 0.0L ? false : true; - case STRING: - case UNSPECIFIED: - return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L); - case NONE: - default: - return SGRawValue::DefaultValue; - } -} - -int -SGPropertyNode::getIntValue () const -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == INT) - return get_int(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue; - switch (_type) { - case ALIAS: - return _value.alias->getIntValue(); - case BOOL: - return int(get_bool()); - case INT: - return get_int(); - case LONG: - return int(get_long()); - case FLOAT: - return int(get_float()); - case DOUBLE: - return int(get_double()); - case STRING: - case UNSPECIFIED: - return atoi(get_string()); - case NONE: - default: - return SGRawValue::DefaultValue; - } -} - -long -SGPropertyNode::getLongValue () const -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == LONG) - return get_long(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue; - switch (_type) { - case ALIAS: - return _value.alias->getLongValue(); - case BOOL: - return long(get_bool()); - case INT: - return long(get_int()); - case LONG: - return get_long(); - case FLOAT: - return long(get_float()); - case DOUBLE: - return long(get_double()); - case STRING: - case UNSPECIFIED: - return strtol(get_string(), 0, 0); - case NONE: - default: - return SGRawValue::DefaultValue; - } -} - -float -SGPropertyNode::getFloatValue () const -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == FLOAT) - return get_float(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue; - switch (_type) { - case ALIAS: - return _value.alias->getFloatValue(); - case BOOL: - return float(get_bool()); - case INT: - return float(get_int()); - case LONG: - return float(get_long()); - case FLOAT: - return get_float(); - case DOUBLE: - return float(get_double()); - case STRING: - case UNSPECIFIED: - return atof(get_string()); - case NONE: - default: - return SGRawValue::DefaultValue; - } -} - -double -SGPropertyNode::getDoubleValue () const -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == DOUBLE) - return get_double(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue; - - switch (_type) { - case ALIAS: - return _value.alias->getDoubleValue(); - case BOOL: - return double(get_bool()); - case INT: - return double(get_int()); - case LONG: - return double(get_long()); - case FLOAT: - return double(get_float()); - case DOUBLE: - return get_double(); - case STRING: - case UNSPECIFIED: - return strtod(get_string(), 0); - case NONE: - default: - return SGRawValue::DefaultValue; - } -} - -const char * -SGPropertyNode::getStringValue () const -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == STRING) - return get_string(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue; - return make_string(); -} - -bool -SGPropertyNode::setBoolValue (bool value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == BOOL) - return set_bool(value); - - bool result = false; - TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _tied = false; - _type = BOOL; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setBoolValue(value); - break; - case BOOL: - result = set_bool(value); - break; - case INT: - result = set_int(int(value)); - break; - case LONG: - result = set_long(long(value)); - break; - case FLOAT: - result = set_float(float(value)); - break; - case DOUBLE: - result = set_double(double(value)); - break; - case STRING: - case UNSPECIFIED: - result = set_string(value ? "true" : "false"); - break; - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setIntValue (int value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == INT) - return set_int(value); - - bool result = false; - TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = INT; - _local_val.int_val = 0; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setIntValue(value); - break; - case BOOL: - result = set_bool(value == 0 ? false : true); - break; - case INT: - result = set_int(value); - break; - case LONG: - result = set_long(long(value)); - break; - case FLOAT: - result = set_float(float(value)); - break; - case DOUBLE: - result = set_double(double(value)); - break; - case STRING: - case UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%d", value); - result = set_string(buf); - break; - } - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setLongValue (long value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == LONG) - return set_long(value); - - bool result = false; - TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = LONG; - _local_val.long_val = 0L; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setLongValue(value); - break; - case BOOL: - result = set_bool(value == 0L ? false : true); - break; - case INT: - result = set_int(int(value)); - break; - case LONG: - result = set_long(value); - break; - case FLOAT: - result = set_float(float(value)); - break; - case DOUBLE: - result = set_double(double(value)); - break; - case STRING: - case UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%ld", value); - result = set_string(buf); - break; - } - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setFloatValue (float value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == FLOAT) - return set_float(value); - - bool result = false; - TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = FLOAT; - _local_val.float_val = 0; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setFloatValue(value); - break; - case BOOL: - result = set_bool(value == 0.0 ? false : true); - break; - case INT: - result = set_int(int(value)); - break; - case LONG: - result = set_long(long(value)); - break; - case FLOAT: - result = set_float(value); - break; - case DOUBLE: - result = set_double(double(value)); - break; - case STRING: - case UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%f", value); - result = set_string(buf); - break; - } - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setDoubleValue (double value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == DOUBLE) - return set_double(value); - - bool result = false; - TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _local_val.double_val = value; - _type = DOUBLE; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setDoubleValue(value); - break; - case BOOL: - result = set_bool(value == 0.0L ? false : true); - break; - case INT: - result = set_int(int(value)); - break; - case LONG: - result = set_long(long(value)); - break; - case FLOAT: - result = set_float(float(value)); - break; - case DOUBLE: - result = set_double(value); - break; - case STRING: - case UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%f", value); - result = set_string(buf); - break; - } - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setStringValue (const char * value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == STRING) - return set_string(value); - - bool result = false; - TEST_WRITE; - if (_type == NONE || _type == UNSPECIFIED) { - clear_value(); - _type = STRING; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setStringValue(value); - break; - case BOOL: - result = set_bool((compare_strings(value, "true") - || atoi(value)) ? true : false); - break; - case INT: - result = set_int(atoi(value)); - break; - case LONG: - result = set_long(strtol(value, 0, 0)); - break; - case FLOAT: - result = set_float(atof(value)); - break; - case DOUBLE: - result = set_double(strtod(value, 0)); - break; - case STRING: - case UNSPECIFIED: - result = set_string(value); - break; - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setUnspecifiedValue (const char * value) -{ - bool result = false; - TEST_WRITE; - if (_type == NONE) { - clear_value(); - _type = UNSPECIFIED; - } - - switch (_type) { - case ALIAS: - result = _value.alias->setUnspecifiedValue(value); - break; - case BOOL: - result = set_bool((compare_strings(value, "true") - || atoi(value)) ? true : false); - break; - case INT: - result = set_int(atoi(value)); - break; - case LONG: - result = set_long(strtol(value, 0, 0)); - break; - case FLOAT: - result = set_float(atof(value)); - break; - case DOUBLE: - result = set_double(strtod(value, 0)); - break; - case STRING: - case UNSPECIFIED: - result = set_string(value); - break; - case NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - bool old_val = false; - if (useDefault) - old_val = getBoolValue(); - - clear_value(); - _type = BOOL; - _tied = true; - _value.bool_val = rawValue.clone(); - - if (useDefault) - setBoolValue(old_val); - - return true; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - int old_val = 0; - if (useDefault) - old_val = getIntValue(); - - clear_value(); - _type = INT; - _tied = true; - _value.int_val = rawValue.clone(); - - if (useDefault) - setIntValue(old_val); - - return true; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - long old_val = 0; - if (useDefault) - old_val = getLongValue(); - - clear_value(); - _type = LONG; - _tied = true; - _value.long_val = rawValue.clone(); - - if (useDefault) - setLongValue(old_val); - - return true; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - float old_val = 0.0; - if (useDefault) - old_val = getFloatValue(); - - clear_value(); - _type = FLOAT; - _tied = true; - _value.float_val = rawValue.clone(); - - if (useDefault) - setFloatValue(old_val); - - return true; -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - double old_val = 0.0; - if (useDefault) - old_val = getDoubleValue(); - - clear_value(); - _type = DOUBLE; - _tied = true; - _value.double_val = rawValue.clone(); - - if (useDefault) - setDoubleValue(old_val); - - return true; - -} - -bool -SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) -{ - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - string old_val; - if (useDefault) - old_val = getStringValue(); - - clear_value(); - _type = STRING; - _tied = true; - _value.string_val = rawValue.clone(); - - if (useDefault) - setStringValue(old_val.c_str()); - - return true; -} - -bool -SGPropertyNode::untie () -{ - if (!_tied) - return false; - - switch (_type) { - case BOOL: { - bool val = getBoolValue(); - clear_value(); - _type = BOOL; - _local_val.bool_val = val; - break; - } - case INT: { - int val = getIntValue(); - clear_value(); - _type = INT; - _local_val.int_val = val; - break; - } - case LONG: { - long val = getLongValue(); - clear_value(); - _type = LONG; - _local_val.long_val = val; - break; - } - case FLOAT: { - float val = getFloatValue(); - clear_value(); - _type = FLOAT; - _local_val.float_val = val; - break; - } - case DOUBLE: { - double val = getDoubleValue(); - clear_value(); - _type = DOUBLE; - _local_val.double_val = val; - break; - } - case STRING: - case UNSPECIFIED: { - string val = getStringValue(); - clear_value(); - _type = STRING; - _local_val.string_val = copy_string(val.c_str()); - break; - } - case NONE: - default: - break; - } - - _tied = false; - return true; -} - -SGPropertyNode * -SGPropertyNode::getRootNode () -{ - if (_parent == 0) - return this; - else - return _parent->getRootNode(); -} - -const SGPropertyNode * -SGPropertyNode::getRootNode () const -{ - if (_parent == 0) - return this; - else - return _parent->getRootNode(); -} - -SGPropertyNode * -SGPropertyNode::getNode (const char * relative_path, bool create) -{ - if (_path_cache == 0) - _path_cache = new hash_table; - - SGPropertyNode * result = _path_cache->get(relative_path); - if (result == 0) { - vector components; - parse_path(relative_path, components); - result = find_node(this, components, 0, create); - if (result != 0) - _path_cache->put(relative_path, result); - } - - return result; -} - -SGPropertyNode * -SGPropertyNode::getNode (const char * relative_path, int index, bool create) -{ - vector components; - parse_path(relative_path, components); - if (components.size() > 0) - components[components.size()-1].index = index; - return find_node(this, components, 0, create); -} - -const SGPropertyNode * -SGPropertyNode::getNode (const char * relative_path) const -{ - return ((SGPropertyNode *)this)->getNode(relative_path, false); -} - -const SGPropertyNode * -SGPropertyNode::getNode (const char * relative_path, int index) const -{ - return ((SGPropertyNode *)this)->getNode(relative_path, index, false); -} - - -//////////////////////////////////////////////////////////////////////// -// Convenience methods using relative paths. -//////////////////////////////////////////////////////////////////////// - - -/** - * Test whether another node has a value attached. - */ -bool -SGPropertyNode::hasValue (const char * relative_path) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? false : node->hasValue()); -} - - -/** - * Get the value type for another node. - */ -SGPropertyNode::Type -SGPropertyNode::getType (const char * relative_path) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? UNSPECIFIED : (Type)(node->getType())); -} - - -/** - * Get a bool value for another node. - */ -bool -SGPropertyNode::getBoolValue (const char * relative_path, - bool defaultValue) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getBoolValue()); -} - - -/** - * Get an int value for another node. - */ -int -SGPropertyNode::getIntValue (const char * relative_path, - int defaultValue) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getIntValue()); -} - - -/** - * Get a long value for another node. - */ -long -SGPropertyNode::getLongValue (const char * relative_path, - long defaultValue) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getLongValue()); -} - - -/** - * Get a float value for another node. - */ -float -SGPropertyNode::getFloatValue (const char * relative_path, - float defaultValue) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getFloatValue()); -} - - -/** - * Get a double value for another node. - */ -double -SGPropertyNode::getDoubleValue (const char * relative_path, - double defaultValue) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getDoubleValue()); -} - - -/** - * Get a string value for another node. - */ -const char * -SGPropertyNode::getStringValue (const char * relative_path, - const char * defaultValue) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getStringValue()); -} - - -/** - * Set a bool value for another node. - */ -bool -SGPropertyNode::setBoolValue (const char * relative_path, bool value) -{ - return getNode(relative_path, true)->setBoolValue(value); -} - - -/** - * Set an int value for another node. - */ -bool -SGPropertyNode::setIntValue (const char * relative_path, int value) -{ - return getNode(relative_path, true)->setIntValue(value); -} - - -/** - * Set a long value for another node. - */ -bool -SGPropertyNode::setLongValue (const char * relative_path, long value) -{ - return getNode(relative_path, true)->setLongValue(value); -} - - -/** - * Set a float value for another node. - */ -bool -SGPropertyNode::setFloatValue (const char * relative_path, float value) -{ - return getNode(relative_path, true)->setFloatValue(value); -} - - -/** - * Set a double value for another node. - */ -bool -SGPropertyNode::setDoubleValue (const char * relative_path, double value) -{ - return getNode(relative_path, true)->setDoubleValue(value); -} - - -/** - * Set a string value for another node. - */ -bool -SGPropertyNode::setStringValue (const char * relative_path, const char * value) -{ - return getNode(relative_path, true)->setStringValue(value); -} - - -/** - * Set an unknown value for another node. - */ -bool -SGPropertyNode::setUnspecifiedValue (const char * relative_path, - const char * value) -{ - return getNode(relative_path, true)->setUnspecifiedValue(value); -} - - -/** - * Test whether another node is tied. - */ -bool -SGPropertyNode::isTied (const char * relative_path) const -{ - const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? false : node->isTied()); -} - - -/** - * Tie a node reached by a relative path, creating it if necessary. - */ -bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) -{ - return getNode(relative_path, true)->tie(rawValue, useDefault); -} - - -/** - * Tie a node reached by a relative path, creating it if necessary. - */ -bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) -{ - return getNode(relative_path, true)->tie(rawValue, useDefault); -} - - -/** - * Tie a node reached by a relative path, creating it if necessary. - */ -bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) -{ - return getNode(relative_path, true)->tie(rawValue, useDefault); -} - - -/** - * Tie a node reached by a relative path, creating it if necessary. - */ -bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) -{ - return getNode(relative_path, true)->tie(rawValue, useDefault); -} - - -/** - * Tie a node reached by a relative path, creating it if necessary. - */ -bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) -{ - return getNode(relative_path, true)->tie(rawValue, useDefault); -} - - -/** - * Tie a node reached by a relative path, creating it if necessary. - */ -bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) -{ - return getNode(relative_path, true)->tie(rawValue, useDefault); -} - - -/** - * Attempt to untie another node reached by a relative path. - */ -bool -SGPropertyNode::untie (const char * relative_path) -{ - SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? false : node->untie()); -} - -void -SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener) -{ - if (_listeners == 0) - _listeners = new vector; - _listeners->push_back(listener); - listener->register_property(this); -} - -void -SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener) -{ - vector::iterator it = - find(_listeners->begin(), _listeners->end(), listener); - if (it != _listeners->end()) { - _listeners->erase(it); - listener->unregister_property(this); - if (_listeners->empty()) { - vector * 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); -} - - - -//////////////////////////////////////////////////////////////////////// -// 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; -} - - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGPropertyChangeListener. -//////////////////////////////////////////////////////////////////////// - -SGPropertyChangeListener::~SGPropertyChangeListener () -{ - // This will come back and remove - // the current item each time. Is - // that OK? - vector::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::iterator it = - find(_properties.begin(), _properties.end(), node); - if (it != _properties.end()) - _properties.erase(it); -} - - -// end of props.cxx diff --git a/simgear/misc/props.hxx b/simgear/misc/props.hxx deleted file mode 100644 index a042c61d..00000000 --- a/simgear/misc/props.hxx +++ /dev/null @@ -1,1337 +0,0 @@ -/** - * \file props.hxx - * Interface definition for a property list. - * Started Fall 2000 by David Megginson, david@megginson.com - * This code is released into the Public Domain. - * - * See props.html for documentation [replace with URL when available]. - * - * $Id$ - */ - -#ifndef __PROPS_HXX -#define __PROPS_HXX - -#ifndef PROPS_STANDALONE -#define PROPS_STANDALONE 0 -#endif - -#include - -#if PROPS_STANDALONE - -#include -#include - -using std::string; -using std::vector; -using std::istream; -using std::ostream; - -#else - -#include -#include -#include STL_STRING -#include STL_IOSTREAM -SG_USING_STD(string); -SG_USING_STD(vector); -SG_USING_STD(istream); -SG_USING_STD(ostream); - -#endif - - -#ifdef NONE -#pragma warn A sloppy coder has defined NONE as a macro! -#undef NONE -#endif - -#ifdef ALIAS -#pragma warn A sloppy coder has defined ALIAS as a macro! -#undef ALIAS -#endif - -#ifdef UNSPECIFIED -#pragma warn A sloppy coder has defined UNSPECIFIED as a macro! -#undef UNSPECIFIED -#endif - -#ifdef BOOL -#pragma warn A sloppy coder has defined BOOL as a macro! -#undef BOOL -#endif - -#ifdef INT -#pragma warn A sloppy coder has defined INT as a macro! -#undef INT -#endif - -#ifdef LONG -#pragma warn A sloppy coder has defined LONG as a macro! -#undef LONG -#endif - -#ifdef FLOAT -#pragma warn A sloppy coder has defined FLOAT as a macro! -#undef FLOAT -#endif - -#ifdef DOUBLE -#pragma warn A sloppy coder has defined DOUBLE as a macro! -#undef DOUBLE -#endif - -#ifdef STRING -#pragma warn A sloppy coder has defined STRING as a macro! -#undef STRING -#endif - - - -//////////////////////////////////////////////////////////////////////// -// A raw value. -// -// This is the mechanism that information-providing routines can -// use to link their own values to the property manager. Any -// SGValue can be tied to a raw value and then untied again. -// -// Note: we are forced to use inlined methods here to ensure -// that the templates will be instantiated. We're probably taking -// a small performance hit for that. -//////////////////////////////////////////////////////////////////////// - - -/** - * 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.

- * - *

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 - * frequently during a session as a value is retyped or bound and - * unbound to various data source, but the abstract SGPropertyNode - * layer insulates the application from those changes. The raw value - * contains no facilities for data binding or for type conversion: it - * is simply the abstraction of a primitive data type (or a compound - * data type, in the case of a string).

- * - *

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.

- * - *

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

- * - * @see SGPropertyNode - * @see SGRawValuePointer - * @see SGRawValueFunctions - * @see SGRawValueFunctionsIndexed - * @see SGRawValueMethods - * @see SGRawValueMethodsIndexed - */ -template -class SGRawValue -{ -public: - - /** - * The default underlying value for this type. - * - * Every raw value has a default; the default is false for a - * boolean, 0 for the various numeric values, and "" for a string. - * If additional types of raw values are added in the future, they - * 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. - - - /** - * Constructor. - * - * Use the default value for this type. - */ - SGRawValue () {} - - - /** - * Destructor. - */ - virtual ~SGRawValue () {} - - - /** - * Return the underlying value. - * - * @return The actual value for the property. - * @see #setValue - */ - virtual T getValue () const = 0; - - - /** - * Assign a new underlying value. - * - * If the new value cannot be set (because this is a read-only - * raw value, or because the new value is not acceptable for - * some reason) this method returns false and leaves the original - * value unchanged. - * - * @param value The actual value for the property. - * @return true if the value was set successfully, false otherwise. - * @see #getValue - */ - virtual bool setValue (T value) = 0; - - - /** - * Create a new deep copy of this raw value. - * - * The copy will contain its own version of the underlying value - * as well. - * - * @return A deep copy of the current object. - */ - virtual SGRawValue * clone () const = 0; -}; - - -/** - * A raw value bound to a pointer. - * - * This is the most efficient way to tie an external value, but also - * the most dangerous, because there is no way for the supplier to - * perform bounds checking and derived calculations except by polling - * the variable to see if it has changed. There is no default - * constructor, because this class would be meaningless without a - * pointer. - */ -template -class SGRawValuePointer : public SGRawValue -{ -public: - - /** - * Explicit pointer constructor. - * - * Create a new raw value bound to the value of the variable - * referenced by the pointer. - * - * @param ptr The pointer to the variable to which this raw value - * will be bound. - */ - SGRawValuePointer (T * ptr) : _ptr(ptr) {} - - /** - * Destructor. - */ - virtual ~SGRawValuePointer () {} - - /** - * Get the underlying value. - * - * This method will dereference the pointer and return the - * variable's value. - */ - virtual T getValue () const { return *_ptr; } - - /** - * Set the underlying value. - * - * This method will dereference the pointer and change the - * variable's value. - */ - virtual bool setValue (T value) { *_ptr = value; return true; } - - /** - * Create a copy of this raw value. - * - * The copy will use the same external pointer as the original. - */ - virtual SGRawValue * clone () const { - return new SGRawValuePointer(_ptr); - } - -private: - T * _ptr; -}; - - -/** - * A value managed through static functions. - * - * A read-only value will not have a setter; a write-only value will - * not have a getter. - */ -template -class SGRawValueFunctions : public SGRawValue -{ -public: - - /** - * The template type of a static getter function. - */ - typedef T (*getter_t)(); - - /** - * The template type of a static setter function. - */ - typedef void (*setter_t)(T); - - /** - * Explicit constructor. - * - * Create a new raw value bound to the getter and setter supplied. - * - * @param getter A static function for getting a value, or 0 - * to read-disable the value. - * @param setter A static function for setting a value, or 0 - * to write-disable the value. - */ - SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0) - : _getter(getter), _setter(setter) {} - - /** - * Destructor. - */ - virtual ~SGRawValueFunctions () {} - - /** - * Get the underlying value. - * - * This method will invoke the getter function to get a value. - * If no getter function was supplied, this method will always - * return the default value for the type. - */ - virtual T getValue () const { - if (_getter) return (*_getter)(); - else return SGRawValue::DefaultValue; - } - - /** - * Set the underlying value. - * - * This method will invoke the setter function to change the - * underlying value. If no setter function was supplied, this - * method will return false. - */ - virtual bool setValue (T value) { - if (_setter) { (*_setter)(value); return true; } - else return false; - } - - /** - * Create a copy of this raw value, bound to the same functions. - */ - virtual SGRawValue * clone () const { - return new SGRawValueFunctions(_getter,_setter); - } - -private: - getter_t _getter; - setter_t _setter; -}; - - -/** - * An indexed value bound to static functions. - * - * A read-only value will not have a setter; a write-only value will - * not have a getter. An indexed value is useful for binding one - * of a list of possible values (such as multiple engines for a - * plane). The index is hard-coded at creation time. - * - * @see SGRawValue - */ -template -class SGRawValueFunctionsIndexed : public SGRawValue -{ -public: - typedef T (*getter_t)(int); - typedef void (*setter_t)(int,T); - SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0) - : _index(index), _getter(getter), _setter(setter) {} - virtual ~SGRawValueFunctionsIndexed () {} - virtual T getValue () const { - if (_getter) return (*_getter)(_index); - else return SGRawValue::DefaultValue; - } - virtual bool setValue (T value) { - if (_setter) { (*_setter)(_index, value); return true; } - else return false; - } - virtual SGRawValue * clone () const { - return new SGRawValueFunctionsIndexed(_index, _getter, _setter); - } -private: - int _index; - getter_t _getter; - setter_t _setter; -}; - - -/** - * A value managed through an object and access methods. - * - * A read-only value will not have a setter; a write-only value will - * not have a getter. - */ -template -class SGRawValueMethods : public SGRawValue -{ -public: - typedef T (C::*getter_t)() const; - typedef void (C::*setter_t)(T); - SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0) - : _obj(obj), _getter(getter), _setter(setter) {} - virtual ~SGRawValueMethods () {} - virtual T getValue () const { - if (_getter) { return (_obj.*_getter)(); } - else { return SGRawValue::DefaultValue; } - } - virtual bool setValue (T value) { - if (_setter) { (_obj.*_setter)(value); return true; } - else return false; - } - virtual SGRawValue * clone () const { - return new SGRawValueMethods(_obj, _getter, _setter); - } -private: - C &_obj; - getter_t _getter; - setter_t _setter; -}; - - -/** - * An indexed value managed through an object and access methods. - * - * A read-only value will not have a setter; a write-only value will - * not have a getter. - */ -template -class SGRawValueMethodsIndexed : public SGRawValue -{ -public: - typedef T (C::*getter_t)(int) const; - typedef void (C::*setter_t)(int, T); - SGRawValueMethodsIndexed (C &obj, int index, - getter_t getter = 0, setter_t setter = 0) - : _obj(obj), _index(index), _getter(getter), _setter(setter) {} - virtual ~SGRawValueMethodsIndexed () {} - virtual T getValue () const { - if (_getter) { return (_obj.*_getter)(_index); } - else { return SGRawValue::DefaultValue; } - } - virtual bool setValue (T value) { - if (_setter) { (_obj.*_setter)(_index, value); return true; } - else return false; - } - virtual SGRawValue * clone () const { - return new SGRawValueMethodsIndexed(_obj, _index, _getter, _setter); - } -private: - C &_obj; - int _index; - getter_t _getter; - setter_t _setter; -}; - - -/** - * The smart pointer that manage reference counting - */ -class SGPropertyNode; -class SGPropertyNode_ptr -{ -public: - - /** - * Default constructor - */ - SGPropertyNode_ptr(); - - /** - * Copy constructor - */ - SGPropertyNode_ptr( const SGPropertyNode_ptr &r ); - - /** - * Constructor from a pointer to a node - */ - SGPropertyNode_ptr( SGPropertyNode *p ); - - /** - * Destructor - */ - ~SGPropertyNode_ptr(); - - /** - * Assignement operator - */ - SGPropertyNode_ptr &operator=( const SGPropertyNode_ptr &r ); - - /** - * Pointer access operator - */ - SGPropertyNode *operator->(); - - /** - * Pointer access operator (const) - */ - const SGPropertyNode *operator->() const; - - /** - * Conversion to SGPropertyNode * operator - */ - operator SGPropertyNode *(); - - /** - * Conversion to const SGPropertyNode * operator - */ - operator const SGPropertyNode *() const; - - /** - * Return the pointer. - */ - SGPropertyNode * ptr () { return _ptr; } - - /** - * Validity test - */ - bool valid() const; - -private: - - SGPropertyNode *_ptr; -}; - - - -/** - * The property change listener interface. - * - *

Any class that needs to listen for property changes must implement - * this interface.

- */ -class SGPropertyChangeListener -{ -public: - virtual ~SGPropertyChangeListener (); - virtual void valueChanged (SGPropertyNode * node); - virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child); - virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child); - -protected: - friend class SGPropertyNode; - virtual void register_property (SGPropertyNode * node); - virtual void unregister_property (SGPropertyNode * node); - -private: - vector _properties; -}; - - - -/** - * A node in a property tree. - */ -class SGPropertyNode -{ -public: - - /** - * Public constants. - */ - enum { - MAX_STRING_LEN = 1024 - }; - - /** - * Property value types. - */ - enum Type { - NONE, - ALIAS, - BOOL, - INT, - LONG, - FLOAT, - DOUBLE, - STRING, - UNSPECIFIED - }; - - - /** - * Access mode attributes. - * - *

The ARCHIVE attribute is strictly advisory, and controls - * whether the property should normally be saved and restored.

- */ - enum Attribute { - READ = 1, - WRITE = 2, - ARCHIVE = 4, - REMOVED = 8, - TRACE_READ = 16, - TRACE_WRITE = 32 - }; - - - /** - * Last used attribute - * Update as needed when enum Attribute is changed - */ - static const int LAST_USED_ATTRIBUTE; - - - /** - * Default constructor. - */ - SGPropertyNode (); - - - /** - * Copy constructor. - */ - SGPropertyNode (const SGPropertyNode &node); - - - /** - * Destructor. - */ - virtual ~SGPropertyNode (); - - - - // - // Basic properties. - // - - /** - * Test whether this node contains a primitive leaf value. - */ - bool hasValue () const { return (_type != NONE); } - - - /** - * Get the node's simple (XML) name. - */ - const char * getName () const { return _name; } - - - /** - * Get the node's pretty display name, with subscript when needed. - */ - const char * getDisplayName (bool simplify = false) const; - - - /** - * Get the node's integer index. - */ - int getIndex () const { return _index; } - - - /** - * Get a non-const pointer to the node's parent. - */ - SGPropertyNode * getParent () { return _parent; } - - - /** - * Get a const pointer to the node's parent. - */ - const SGPropertyNode * getParent () const { return _parent; } - - - // - // Children. - // - - - /** - * Get the number of child nodes. - */ - int nChildren () const { return _children.size(); } - - - /** - * Get a child node by position (*NOT* index). - */ - SGPropertyNode * getChild (int position); - - - /** - * Get a const child node by position (*NOT* index). - */ - const SGPropertyNode * getChild (int position) const; - - - /** - * Test whether a named child exists. - */ - bool hasChild (const char * name, int index = 0) const - { - return (getChild(name, index) != 0); - } - - - /** - * Get a child node by name and index. - */ - SGPropertyNode * getChild (const char * name, int index = 0, - bool create = false); - - - /** - * Get a const child node by name and index. - */ - const SGPropertyNode * getChild (const char * name, int index = 0) const; - - - /** - * Get a vector of all children with the specified name. - */ - vector getChildren (const char * name) const; - - - /** - * Remove a child node - */ - SGPropertyNode_ptr removeChild (const char * name, int index = 0, - bool keep = true); - - - // - // Alias support. - // - - - /** - * Alias this node's leaf value to another's. - */ - bool alias (SGPropertyNode * target); - - - /** - * Alias this node's leaf value to another's by relative path. - */ - bool alias (const char * path); - - - /** - * Remove any alias for this node. - */ - bool unalias (); - - - /** - * Test whether the node's leaf value is aliased to another's. - */ - bool isAlias () const { return (_type == ALIAS); } - - - /** - * Get a non-const pointer to the current alias target, if any. - */ - SGPropertyNode * getAliasTarget (); - - - /** - * Get a const pointer to the current alias target, if any. - */ - const SGPropertyNode * getAliasTarget () const; - - - // - // Path information. - // - - - /** - * Get the path to this node from the root. - */ - const char * getPath (bool simplify = false) const; - - - /** - * Get a pointer to the root node. - */ - SGPropertyNode * getRootNode (); - - - /** - * Get a const pointer to the root node. - */ - const SGPropertyNode * getRootNode () const; - - - /** - * Get a pointer to another node by relative path. - */ - SGPropertyNode * getNode (const char * relative_path, bool create = false); - - - /** - * Get a pointer to another node by relative path. - * - * This method leaves the index off the last member of the path, - * so that the user can specify it separately (and save some - * string building). For example, getNode("/bar[1]/foo", 3) is - * exactly equivalent to getNode("bar[1]/foo[3]"). The index - * provided overrides any given in the path itself for the last - * component. - */ - SGPropertyNode * getNode (const char * relative_path, int index, - bool create = false); - - - /** - * Get a const pointer to another node by relative path. - */ - const SGPropertyNode * getNode (const char * relative_path) const; - - - /** - * Get a const pointer to another node by relative path. - * - * This method leaves the index off the last member of the path, - * so that the user can specify it separate. - */ - const SGPropertyNode * getNode (const char * relative_path, - int index) const; - - - // - // Access Mode. - // - - /** - * Check a single mode attribute for the property node. - */ - bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); } - - - /** - * Set a single mode attribute for the property node. - */ - void setAttribute (Attribute attr, bool state) { - (state ? _attr |= attr : _attr &= ~attr); - } - - - /** - * Get all of the mode attributes for the property node. - */ - int getAttributes () const { return _attr; } - - - /** - * Set all of the mode attributes for the property node. - */ - void setAttributes (int attr) { _attr = attr; } - - - // - // Leaf Value (primitive). - // - - - /** - * Get the type of leaf value, if any, for this node. - */ - Type getType () const; - - - /** - * Get a bool value for this node. - */ - bool getBoolValue () const; - - - /** - * Get an int value for this node. - */ - int getIntValue () const; - - - /** - * Get a long int value for this node. - */ - long getLongValue () const; - - - /** - * Get a float value for this node. - */ - float getFloatValue () const; - - - /** - * Get a double value for this node. - */ - double getDoubleValue () const; - - - /** - * Get a string value for this node. - */ - const char * getStringValue () const; - - - - /** - * Set a bool value for this node. - */ - bool setBoolValue (bool value); - - - /** - * Set an int value for this node. - */ - bool setIntValue (int value); - - - /** - * Set a long int value for this node. - */ - bool setLongValue (long value); - - - /** - * Set a float value for this node. - */ - bool setFloatValue (float value); - - - /** - * Set a double value for this node. - */ - bool setDoubleValue (double value); - - - /** - * Set a string value for this node. - */ - bool setStringValue (const char * value); - - - /** - * Set a value of unspecified type for this node. - */ - bool setUnspecifiedValue (const char * value); - - - // - // Data binding. - // - - - /** - * Test whether this node is bound to an external data source. - */ - bool isTied () const { return _tied; } - - - /** - * Bind this node to an external bool source. - */ - bool tie (const SGRawValue &rawValue, bool useDefault = true); - - - /** - * Bind this node to an external int source. - */ - bool tie (const SGRawValue &rawValue, bool useDefault = true); - - - /** - * Bind this node to an external long int source. - */ - bool tie (const SGRawValue &rawValue, bool useDefault = true); - - - /** - * Bind this node to an external float source. - */ - bool tie (const SGRawValue &rawValue, bool useDefault = true); - - - /** - * Bind this node to an external double source. - */ - bool tie (const SGRawValue &rawValue, bool useDefault = true); - - - /** - * Bind this node to an external string source. - */ - bool tie (const SGRawValue &rawValue, bool useDefault = true); - - - /** - * Unbind this node from any external data source. - */ - bool untie (); - - - // - // Convenience methods using paths. - // TODO: add attribute methods - // - - - /** - * Get another node's type. - */ - Type getType (const char * relative_path) const; - - - /** - * Test whether another node has a leaf value. - */ - bool hasValue (const char * relative_path) const; - - - /** - * Get another node's value as a bool. - */ - bool getBoolValue (const char * relative_path, - bool defaultValue = false) const; - - - /** - * Get another node's value as an int. - */ - int getIntValue (const char * relative_path, - int defaultValue = 0) const; - - - /** - * Get another node's value as a long int. - */ - long getLongValue (const char * relative_path, - long defaultValue = 0L) const; - - - /** - * Get another node's value as a float. - */ - float getFloatValue (const char * relative_path, - float defaultValue = 0.0) const; - - - /** - * Get another node's value as a double. - */ - double getDoubleValue (const char * relative_path, - double defaultValue = 0.0L) const; - - - /** - * Get another node's value as a string. - */ - const char * getStringValue (const char * relative_path, - const char * defaultValue = "") const; - - - /** - * Set another node's value as a bool. - */ - bool setBoolValue (const char * relative_path, bool value); - - - /** - * Set another node's value as an int. - */ - bool setIntValue (const char * relative_path, int value); - - - /** - * Set another node's value as a long int. - */ - bool setLongValue (const char * relative_path, long value); - - - /** - * Set another node's value as a float. - */ - bool setFloatValue (const char * relative_path, float value); - - - /** - * Set another node's value as a double. - */ - bool setDoubleValue (const char * relative_path, double value); - - - /** - * Set another node's value as a string. - */ - bool setStringValue (const char * relative_path, const char * value); - - - /** - * Set another node's value with no specified type. - */ - bool setUnspecifiedValue (const char * relative_path, const char * value); - - - /** - * Test whether another node is bound to an external data source. - */ - bool isTied (const char * relative_path) const; - - - /** - * Bind another node to an external bool source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - - /** - * Bind another node to an external int source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - - /** - * Bind another node to an external long int source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - - /** - * Bind another node to an external float source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - - /** - * Bind another node to an external double source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - - /** - * Bind another node to an external string source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - - /** - * Unbind another node from any external data source. - */ - bool untie (const char * relative_path); - - - /** - * Add a change listener to the property. - */ - void addChangeListener (SGPropertyChangeListener * listener); - - - /** - * Remove a change listener from the property. - */ - void removeChangeListener (SGPropertyChangeListener * listener); - - - /** - * Fire a value change event to all listeners. - */ - void fireValueChanged (); - - - /** - * Fire a child-added event to all listeners. - */ - void fireChildAdded (SGPropertyNode * child); - - - /** - * Fire a child-removed event to all listeners. - */ - void fireChildRemoved (SGPropertyNode * child); - - -protected: - - void fireValueChanged (SGPropertyNode * node); - void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child); - void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child); - - /** - * Protected constructor for making new nodes on demand. - */ - SGPropertyNode (const char * name, int index, SGPropertyNode * parent); - - -private: - - // Get the raw value - bool get_bool () const; - int get_int () const; - long get_long () const; - float get_float () const; - double get_double () const; - const char * get_string () const; - - // Set the raw value - bool set_bool (bool value); - bool set_int (int value); - bool set_long (long value); - bool set_float (float value); - bool set_double (double value); - bool set_string (const char * value); - - - /** - * Clear any existing value and set the type to NONE. - */ - void clear_value (); - - - /** - * Get the value as a string. - */ - const char * make_string () const; - - - /** - * Trace a read access. - */ - void trace_read () const; - - - /** - * Trace a write access. - */ - void trace_write () const; - - - /** - * Increment reference counter - */ - void incrementRef(); - - /** - * Decrement reference counter - */ - int decrementRef(); - - friend class SGPropertyNode_ptr; - - - mutable char _buffer[MAX_STRING_LEN+1]; - - class hash_table; - - char * _name; - mutable char * _display_name; - int _index; - SGPropertyNode * _parent; - vector _children; - vector _removedChildren; - mutable char * _path; - hash_table * _path_cache; - Type _type; - bool _tied; - int _attr; - int _count; - - // The right kind of pointer... - union { - SGPropertyNode * alias; - SGRawValue * bool_val; - SGRawValue * int_val; - SGRawValue * long_val; - SGRawValue * float_val; - SGRawValue * double_val; - SGRawValue * string_val; - } _value; - - union { - bool bool_val; - int int_val; - long long_val; - float float_val; - double double_val; - char * string_val; - } _local_val; - - vector * _listeners; - - - - /** - * A very simple hash table with no remove functionality. - */ - class hash_table { - public: - - /** - * An entry in a bucket in a hash table. - */ - class entry { - public: - entry (); - virtual ~entry (); - virtual const char * get_key () { return _key; } - virtual void set_key (const char * key); - virtual SGPropertyNode * get_value () { return _value; } - virtual void set_value (SGPropertyNode * value); - private: - char * _key; - SGPropertyNode * _value; - }; - - - /** - * A bucket in a hash table. - */ - class bucket { - public: - bucket (); - virtual ~bucket (); - virtual entry * get_entry (const char * key, bool create = false); - private: - int _length; - entry ** _entries; - }; - - friend class bucket; - - hash_table (); - virtual ~hash_table (); - virtual SGPropertyNode * get (const char * key); - virtual void put (const char * key, SGPropertyNode * value); - - private: - unsigned int hashcode (const char * key); - unsigned int _data_length; - bucket ** _data; - }; - -}; - -#endif // __PROPS_HXX - -// end of props.hxx diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx deleted file mode 100644 index 36ea2b7e..00000000 --- a/simgear/misc/props_io.cxx +++ /dev/null @@ -1,586 +0,0 @@ - -#include - -#include // atof() atoi() - -#include -#include -#include - -#include "sg_path.hxx" -#include "props.hxx" -#include "props_io.hxx" - -#include STL_IOSTREAM -#include STL_FSTREAM -#include STL_STRING -#include -#include - -SG_USING_STD(istream); -SG_USING_STD(ifstream); -SG_USING_STD(ostream); -SG_USING_STD(ofstream); -SG_USING_STD(string); -SG_USING_STD(vector); -SG_USING_STD(map); - -#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE) - - - -//////////////////////////////////////////////////////////////////////// -// Property list visitor, for XML parsing. -//////////////////////////////////////////////////////////////////////// - -class PropsVisitor : public XMLVisitor -{ -public: - - PropsVisitor (SGPropertyNode * root, const string &base) - : _root(root), _level(0), _base(base), _hasException(false) {} - - virtual ~PropsVisitor () {} - - void startXML (); - void endXML (); - void startElement (const char * name, const XMLAttributes &atts); - void endElement (const char * name); - void data (const char * s, int length); - void warning (const char * message, int line, int column); - - bool hasException () const { return _hasException; } - sg_io_exception &getException () { return _exception; } - void setException (const sg_io_exception &exception) { - _exception = exception; - _hasException = true; - } - -private: - - struct State - { - State () : node(0), type(""), mode(DEFAULT_MODE) {} - State (SGPropertyNode * _node, const char * _type, int _mode) - : node(_node), type(_type), mode(_mode) {} - SGPropertyNode * node; - string type; - int mode; - map counters; - }; - - State &state () { return _state_stack[_state_stack.size() - 1]; } - - void push_state (SGPropertyNode * node, const char * type, int mode) { - if (type == 0) - _state_stack.push_back(State(node, "unspecified", mode)); - else - _state_stack.push_back(State(node, type, mode)); - _level++; - _data = ""; - } - - void pop_state () { - _state_stack.pop_back(); - _level--; - } - - string _data; - SGPropertyNode * _root; - int _level; - vector _state_stack; - string _base; - sg_io_exception _exception; - bool _hasException; -}; - -void -PropsVisitor::startXML () -{ - _level = 0; - _state_stack.resize(0); -} - -void -PropsVisitor::endXML () -{ - _level = 0; - _state_stack.resize(0); -} - - -/** - * Check a yes/no flag, with default. - */ -static bool -checkFlag (const char * flag, bool defaultState = true) -{ - if (flag == 0) - return defaultState; - else if (string(flag) == "y") - return true; - else if (string(flag) == "n") - return false; - else { - string message = "Unrecognized flag value '"; - message += flag; - message += '\''; - // FIXME: add location info - throw sg_io_exception(message, "SimGear Property Reader"); - } -} - -void -PropsVisitor::startElement (const char * name, const XMLAttributes &atts) -{ - State &st = state(); - const char * attval; - - if (_level == 0) { - if (string(name) != (string)"PropertyList") { - string message = "Root element name is "; - message += name; - message += "; expected PropertyList"; - throw sg_io_exception(message, "SimGear Property Reader"); - } - - // Check for an include. - attval = atts.getValue("include"); - if (attval != 0) { - SGPath path(SGPath(_base).dir()); - path.append(attval); - try { - readProperties(path.str(), _root); - } catch (sg_io_exception &e) { - setException(e); - } - } - - push_state(_root, "", DEFAULT_MODE); - } - - else { - // Get the index. - attval = atts.getValue("n"); - int index = 0; - if (attval != 0) { - index = atoi(attval); - st.counters[name] = SG_MAX2(st.counters[name], index+1); - } else { - index = st.counters[name]; - st.counters[name]++; - } - - // Got the index, so grab the node. - SGPropertyNode * node = st.node->getChild(name, index, true); - - // Get the access-mode attributes, - // but don't set yet (in case they - // prevent us from recording the value). - int mode = 0; - - attval = atts.getValue("read"); - if (checkFlag(attval, true)) - mode |= SGPropertyNode::READ; - attval = atts.getValue("write"); - if (checkFlag(attval, true)) - mode |= SGPropertyNode::WRITE; - attval = atts.getValue("archive"); - if (checkFlag(attval, false)) - mode |= SGPropertyNode::ARCHIVE; - attval = atts.getValue("trace-read"); - if (checkFlag(attval, false)) - mode |= SGPropertyNode::TRACE_READ; - attval = atts.getValue("trace-write"); - if (checkFlag(attval, false)) - mode |= SGPropertyNode::TRACE_WRITE; - - // Check for an alias. - attval = atts.getValue("alias"); - if (attval != 0) { - if (!node->alias(attval)) - SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval); - } - - // Check for an include. - attval = atts.getValue("include"); - if (attval != 0) { - SGPath path(SGPath(_base).dir()); - path.append(attval); - try { - readProperties(path.str(), node); - } catch (sg_io_exception &e) { - setException(e); - } - } - - push_state(node, atts.getValue("type"), mode); - } -} - -void -PropsVisitor::endElement (const char * name) -{ - State &st = state(); - bool ret; - - // If there are no children and it's - // not an alias, then it's a leaf value. - if (st.node->nChildren() == 0 && !st.node->isAlias()) { - if (st.type == "bool") { - if (_data == "true" || atoi(_data.c_str()) != 0) - ret = st.node->setBoolValue(true); - else - ret = st.node->setBoolValue(false); - } else if (st.type == "int") { - ret = st.node->setIntValue(atoi(_data.c_str())); - } else if (st.type == "long") { - ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0)); - } else if (st.type == "float") { - ret = st.node->setFloatValue(atof(_data.c_str())); - } else if (st.type == "double") { - ret = st.node->setDoubleValue(strtod(_data.c_str(), 0)); - } else if (st.type == "string") { - ret = st.node->setStringValue(_data.c_str()); - } else if (st.type == "unspecified") { - ret = st.node->setUnspecifiedValue(_data.c_str()); - } else { - string message = "Unrecognized data type '"; - message += st.type; - message += '\''; - // FIXME: add location information - throw sg_io_exception(message, "SimGear Property Reader"); - } - if (!ret) - SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set " - << st.node->getPath() << " to value \"" - << _data << "\" with type " << st.type); - } - - // Set the access-mode attributes now, - // once the value has already been - // assigned. - st.node->setAttributes(st.mode); - - pop_state(); -} - -void -PropsVisitor::data (const char * s, int length) -{ - if (state().node->nChildren() == 0) - _data.append(string(s, length)); -} - -void -PropsVisitor::warning (const char * message, int line, int column) -{ - SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: " - << message << " at line " << line << ", column " << column); -} - - - -//////////////////////////////////////////////////////////////////////// -// Property list reader. -//////////////////////////////////////////////////////////////////////// - - -/** - * Read properties from an input stream. - * - * @param input The input stream containing an XML property file. - * @param start_node The root node for reading properties. - * @param base A base path for resolving external include references. - * @return true if the read succeeded, false otherwise. - */ -void -readProperties (istream &input, SGPropertyNode * start_node, - const string &base) -{ - PropsVisitor visitor(start_node, base); - readXML(input, visitor, base); - if (visitor.hasException()) - throw visitor.getException(); -} - - -/** - * Read properties from a file. - * - * @param file A string containing the file path. - * @param start_node The root node for reading properties. - * @return true if the read succeeded, false otherwise. - */ -void -readProperties (const string &file, SGPropertyNode * start_node) -{ - PropsVisitor visitor(start_node, file); - readXML(file, visitor); - if (visitor.hasException()) - throw visitor.getException(); -} - - - -//////////////////////////////////////////////////////////////////////// -// Property list writer. -//////////////////////////////////////////////////////////////////////// - -#define INDENT_STEP 2 - -/** - * Return the type name. - */ -static const char * -getTypeName (SGPropertyNode::Type type) -{ - switch (type) { - case SGPropertyNode::UNSPECIFIED: - return "unspecified"; - case SGPropertyNode::BOOL: - return "bool"; - case SGPropertyNode::INT: - return "int"; - case SGPropertyNode::LONG: - return "long"; - case SGPropertyNode::FLOAT: - return "float"; - case SGPropertyNode::DOUBLE: - return "double"; - case SGPropertyNode::STRING: - return "string"; - case SGPropertyNode::ALIAS: - case SGPropertyNode::NONE: - return "unspecified"; - } - - // keep the compiler from squawking - return "unspecified"; -} - - -/** - * Escape characters for output. - */ -static void -writeData (ostream &output, const string &data) -{ - for (int i = 0; i < (int)data.size(); i++) { - switch (data[i]) { - case '&': - output << "&"; - break; - case '<': - output << "<"; - break; - case '>': - output << ">"; - break; - default: - output << data[i]; - break; - } - } -} - -static void -doIndent (ostream &output, int indent) -{ - while (indent-- > 0) { - output << ' '; - } -} - - -static void -writeAtts (ostream &output, const SGPropertyNode * node) -{ - int index = node->getIndex(); - - if (index != 0) - output << " n=\"" << index << '"'; - -#if 0 - if (!node->getAttribute(SGPropertyNode::READ)) - output << " read=\"n\""; - - if (!node->getAttribute(SGPropertyNode::WRITE)) - output << " write=\"n\""; - - if (node->getAttribute(SGPropertyNode::ARCHIVE)) - output << " archive=\"y\""; -#endif - -} - - -/** - * Test whether a node is archivable or has archivable descendants. - */ -static bool -isArchivable (const SGPropertyNode * node) -{ - // FIXME: it's inefficient to do this all the time - if (node->getAttribute(SGPropertyNode::ARCHIVE)) - return true; - else { - int nChildren = node->nChildren(); - for (int i = 0; i < nChildren; i++) - if (isArchivable(node->getChild(i))) - return true; - } - return false; -} - - -static bool -writeNode (ostream &output, const SGPropertyNode * node, - bool write_all, int indent) -{ - // Don't write the node or any of - // its descendants unless it is - // allowed to be archived. - if (!write_all && !isArchivable(node)) - return true; // Everything's OK, but we won't write. - - const string name = node->getName(); - int nChildren = node->nChildren(); - - // If there is a literal value, - // write it first. - if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) { - doIndent(output, indent); - output << '<' << name; - writeAtts(output, node); - if (node->isAlias() && node->getAliasTarget() != 0) { - output << " alias=\"" << node->getAliasTarget()->getPath() - << "\"/>" << endl; - } else { - if (node->getType() != SGPropertyNode::UNSPECIFIED) - output << " type=\"" << getTypeName(node->getType()) << '"'; - output << '>'; - writeData(output, node->getStringValue()); - output << "' << endl; - } - } - - // If there are children, write them next. - if (nChildren > 0 || node->isAlias()) { - doIndent(output, indent); - output << '<' << name; - writeAtts(output, node); - output << '>' << endl; - for (int i = 0; i < nChildren; i++) - writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP); - doIndent(output, indent); - output << "' << endl; - } - - return true; -} - - -void -writeProperties (ostream &output, const SGPropertyNode * start_node, - bool write_all) -{ - int nChildren = start_node->nChildren(); - - output << "" << endl << endl; - output << "" << endl; - - for (int i = 0; i < nChildren; i++) { - writeNode(output, start_node->getChild(i), write_all, INDENT_STEP); - } - - output << "" << endl; -} - - -void -writeProperties (const string &file, const SGPropertyNode * start_node, - bool write_all) -{ - ofstream output(file.c_str()); - if (output.good()) { - writeProperties(output, start_node, write_all); - } else { - throw sg_io_exception("Cannot open file", sg_location(file)); - } -} - - - -//////////////////////////////////////////////////////////////////////// -// Copy properties from one tree to another. -//////////////////////////////////////////////////////////////////////// - - -/** - * Copy one property tree to another. - * - * @param in The source property tree. - * @param out The destination property tree. - * @return true if all properties were copied, false if some failed - * (for example, if the property's value is tied read-only). - */ -bool -copyProperties (const SGPropertyNode *in, SGPropertyNode *out) -{ - bool retval = true; - - // First, copy the actual value, - // if any. - if (in->hasValue()) { - switch (in->getType()) { - case SGPropertyNode::BOOL: - if (!out->setBoolValue(in->getBoolValue())) - retval = false; - break; - case SGPropertyNode::INT: - if (!out->setIntValue(in->getIntValue())) - retval = false; - break; - case SGPropertyNode::LONG: - if (!out->setLongValue(in->getLongValue())) - retval = false; - break; - case SGPropertyNode::FLOAT: - if (!out->setFloatValue(in->getFloatValue())) - retval = false; - break; - case SGPropertyNode::DOUBLE: - if (!out->setDoubleValue(in->getDoubleValue())) - retval = false; - break; - case SGPropertyNode::STRING: - if (!out->setStringValue(in->getStringValue())) - retval = false; - break; - case SGPropertyNode::UNSPECIFIED: - if (!out->setUnspecifiedValue(in->getStringValue())) - retval = false; - break; - default: - string message = "Unknown internal SGPropertyNode type"; - message += in->getType(); - throw sg_error(message, "SimGear Property Reader"); - } - } - - // Next, copy the children. - int nChildren = in->nChildren(); - for (int i = 0; i < nChildren; i++) { - const SGPropertyNode * in_child = in->getChild(i); - SGPropertyNode * out_child = out->getChild(in_child->getName(), - in_child->getIndex(), - true); - if (!copyProperties(in_child, out_child)) - retval = false; - } - - return retval; -} - -// end of props_io.cxx diff --git a/simgear/misc/props_io.hxx b/simgear/misc/props_io.hxx deleted file mode 100644 index 83e84c5a..00000000 --- a/simgear/misc/props_io.hxx +++ /dev/null @@ -1,66 +0,0 @@ -/** - * \file props_io.hxx - * Interface definition for property list io. - * Started Fall 2000 by David Megginson, david@megginson.com - * This code is released into the Public Domain. - * - * See props.html for documentation [replace with URL when available]. - * - * $Id$ - */ - -#ifndef __PROPS_IO_HXX -#define __PROPS_IO_HXX - -#include -#include - -#include - -#include STL_STRING -#include -#include -#include STL_IOSTREAM - -SG_USING_STD(string); -SG_USING_STD(vector); -SG_USING_STD(map); -SG_USING_STD(istream); -SG_USING_STD(ostream); - -/** - * Read properties from an XML input stream. - */ -void readProperties (istream &input, SGPropertyNode * start_node, - const string &base = ""); - - -/** - * Read properties from an XML file. - */ -void readProperties (const string &file, SGPropertyNode * start_node); - - -/** - * Write properties to an XML output stream. - */ -void writeProperties (ostream &output, const SGPropertyNode * start_node, - bool write_all = false); - - -/** - * Write properties to an XML file. - */ -void writeProperties (const string &file, const SGPropertyNode * start_node, - bool write_all = false); - - -/** - * Copy properties from one node to another. - */ -bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out); - - -#endif // __PROPS_IO_HXX - -// end of props_io.hxx diff --git a/simgear/misc/props_test.cxx b/simgear/misc/props_test.cxx deleted file mode 100644 index ab29deee..00000000 --- a/simgear/misc/props_test.cxx +++ /dev/null @@ -1,346 +0,0 @@ - -//////////////////////////////////////////////////////////////////////// -// Test harness. -//////////////////////////////////////////////////////////////////////// - -#include - -#include STL_IOSTREAM -#include "props.hxx" -#include "props_io.hxx" - -SG_USING_STD(cout); -SG_USING_STD(cerr); -SG_USING_STD(endl); - - - -//////////////////////////////////////////////////////////////////////// -// Sample object. -//////////////////////////////////////////////////////////////////////// - -class Stuff { -public: - Stuff () : _stuff(199.0) {} - virtual float getStuff () const { return _stuff; } - virtual void setStuff (float stuff) { _stuff = stuff; } - virtual float getStuff (int index) const { return _stuff * index; } - virtual void setStuff (int index, float val) { - _stuff = val / index; - } -private: - float _stuff; -}; - - - -//////////////////////////////////////////////////////////////////////// -// Sample function. -//////////////////////////////////////////////////////////////////////// - -static int get100 () { return 100; } - -static double getNum (int index) { return 1.0 / index; } - - - -//////////////////////////////////////////////////////////////////////// -// Show a value. -//////////////////////////////////////////////////////////////////////// - -static void -show_values (const SGPropertyNode * node) -{ - cout << "Bool: " << (node->getBoolValue() ? "true" : "false") << endl; - cout << "Int: " << node->getIntValue() << endl; - cout << "Float: " << node->getFloatValue() << endl; - cout << "Double: " << node->getDoubleValue() << endl; - cout << "String: " << node->getStringValue() << endl; -} - - - -//////////////////////////////////////////////////////////////////////// -// Test individual values. -//////////////////////////////////////////////////////////////////////// - -static void -test_value () -{ - SGPropertyNode * node; - - cout << endl << "Value" << endl << endl; - - // - // Test coercion for getters. - // - - cout << "Testing coercion from bool (expect true)" << endl; - node = new SGPropertyNode; - node->setBoolValue(true); - show_values(node); - delete node; - cout << endl; - - cout << "Testing coercion from int (expect 128)" << endl; - node = new SGPropertyNode; - node->setIntValue(128); - show_values(node); - delete node; - cout << endl; - - cout << "Testing coercion from float (expect 1.0/3.0)" << endl; - node = new SGPropertyNode; - node->setFloatValue(1.0/3.0); - show_values(node); - delete node; - cout << endl; - - cout << "Testing coercion from double (expect 1.0/3.0)" << endl; - node = new SGPropertyNode; - node->setDoubleValue(1.0/3.0); - show_values(node); - delete node; - cout << endl; - - cout << "Testing coercion from string (expect 10e4)" << endl; - node = new SGPropertyNode; - node->setStringValue("10e4"); - show_values(node); - delete node; - cout << endl; - - cout << "Testing coercion from unspecified (expect -10e-4)" << endl; - node = new SGPropertyNode; - node->setUnspecifiedValue("-10e-4"); - show_values(node); - delete node; - cout << endl; - - // - // Test coercion for setters. - // - - node = new SGPropertyNode; - - cout << "Testing coercion to bool from bool (expect false)" << endl; - node->setBoolValue(false); - show_values(node); - cout << endl; - - cout << "Testing coercion to bool from int (expect 1)" << endl; - node->setIntValue(1); - show_values(node); - cout << endl; - - cout << "Testing coercion to bool from float (expect 1.1)" << endl; - node->setFloatValue(1.1); - show_values(node); - cout << endl; - - cout << "Testing coercion to bool from double (expect 1.1)" << endl; - node->setDoubleValue(1.1); - show_values(node); - cout << endl; - - cout << "Testing coercion to bool from string (expect 1e10)" << endl; - node->setStringValue("1e10"); - show_values(node); - cout << endl; - - cout << "Testing coercion to bool from unspecified (expect 1e10)" << endl; - node->setUnspecifiedValue("1e10"); - show_values(node); - cout << endl; - - // Test tying to a pointer. - - static int myValue = 10; - - cout << "Testing tying to a pointer (expect 10)" << endl; - if (!node->tie(SGRawValuePointer(&myValue), false)) - cout << "*** FAILED TO TIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Changing base variable (expect -5)" << endl; - myValue = -5; - show_values(node); - if (!node->untie()) - cout << "*** FAILED TO UNTIE VALUE!!!" << endl; - cout << endl; - - - // Test tying to static functions. - - cout << "Create a new int value (expect 10)" << endl; - node->setIntValue(10); - show_values(node); - cout << endl; - - cout << "Testing tying to static getter (expect 100)" << endl; - if (!node->tie(SGRawValueFunctions(get100))) - cout << "*** FAILED TO TIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Try changing value with no setter (expect 100)" << endl; - if (node->setIntValue(200)) - cout << "*** setIntValue did not return false!!!" << endl; - show_values(node); - cout << endl; - - cout << "Untie value (expect 100)" << endl; - if (!node->untie()) - cout << "*** FAILED TO UNTIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Try changing value (expect 200)" << endl; - if (!node->setIntValue(200)) - cout << "*** setIntValue RETURNED FALSE!!!" << endl; - show_values(node); - cout << endl; - - // Test tying to indexed static functions. - - cout << "Create a new int value (expect 10)" << endl; - node->setIntValue(10); - show_values(node); - cout << endl; - - cout << "Testing tying to indexed static getter (0.3333...)" << endl; - if (!node->tie(SGRawValueFunctionsIndexed(3, getNum))) - cout << "*** FAILED TO TIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Untie value (expect 0.3333...)" << endl; - if (!node->untie()) - cout << "*** FAILED TO UNTIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - - // Test methods. - - cout << "Try tying to an object without defaults (expect 199)" << endl; - Stuff stuff; - SGRawValueMethods tiedstuff(stuff, - &Stuff::getStuff, - &Stuff::setStuff); - if (!node->tie(tiedstuff, false)) - cout << "*** FAILED TO TIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Try untying from object (expect 199)" << endl; - if (!node->untie()) - cout << "*** FAILED TO UNTIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Try tying to an indexed method (expect 199)" << endl; - if (!node->tie(SGRawValueMethodsIndexed - (stuff, 2, &Stuff::getStuff, &Stuff::setStuff))) - cout << "*** FAILED TO TIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - node->untie(); - - cout << "Change value (expect 50)" << endl; - if (!node->setIntValue(50)) - cout << "*** FAILED TO SET VALUE!!!" << endl; - show_values(node); - cout << endl; - - cout << "Try tying to an object with defaults (expect 50)" << endl; - if (!node->tie(tiedstuff, true)) - cout << "*** FAILED TO TIE VALUE!!!" << endl; - show_values(node); - cout << endl; - - delete node; -} - - - -//////////////////////////////////////////////////////////////////////// -// Check property nodes. -//////////////////////////////////////////////////////////////////////// - -static void -dump_node (const SGPropertyNode * node) -{ - writeProperties(cout, node, true); -} - -static void -test_property_nodes () -{ - SGPropertyNode root; - cout << "Created root node " << root.getPath() << endl; - - SGPropertyNode * child = root.getChild("foo", 0, true); - - SGPropertyNode *grandchild = child->getChild("bar", 0, true); - grandchild->setDoubleValue(100); - grandchild = child->getChild("bar", 1, true); - grandchild->setDoubleValue(200); - grandchild = child->getChild("bar", 2, true); - grandchild->setDoubleValue(300); - grandchild = child->getChild("bar", 3, true); - grandchild->setDoubleValue(400); - - child = root.getChild("hack", 0, true); - - grandchild = child->getChild("bar", 0, true); - grandchild->setDoubleValue(100); - grandchild = child->getChild("bar", 3, true); - grandchild->setDoubleValue(200); - grandchild = child->getChild("bar", 1, true); - grandchild->setDoubleValue(300); - grandchild = child->getChild("bar", 2, true); - grandchild->setDoubleValue(400); - dump_node(&root); - - cout << "Trying path (expect /foo[0]/bar[0])" << endl; - grandchild = root.getNode("/hack/../foo/./bar[0]"); - cout << "Path is " << grandchild->getPath() << endl; - cout << endl; - - cout << "Looking for all /hack[0]/bar children" << endl; - vector bar = child->getChildren("bar"); - cout << "There are " << bar.size() << " matches" << endl; - for (int i = 0; i < (int)bar.size(); i++) - cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl; - cout << endl; - - cout << "Testing addition of a totally empty node" << endl; - if (root.getNode("/a/b/c", true) == 0) - cerr << "** failed to create /a/b/c" << endl; - dump_node(&root); - cout << endl; -} - - -int main (int ac, char ** av) -{ - test_value(); - test_property_nodes(); - - for (int i = 1; i < ac; i++) { - try { - cout << "Reading " << av[i] << endl; - SGPropertyNode root; - readProperties(av[i], &root); - writeProperties(cout, &root, true); - cout << endl; - } catch (string &message) { - cout << "Aborted with " << message << endl; - } - } - - return 0; -} diff --git a/simgear/props/Makefile.am b/simgear/props/Makefile.am new file mode 100644 index 00000000..3195e69d --- /dev/null +++ b/simgear/props/Makefile.am @@ -0,0 +1,24 @@ +includedir = @includedir@/props + +lib_LIBRARIES = libsgprops.a + +include_HEADERS = \ + condition.hxx \ + props.hxx \ + props_io.hxx + +libsgprops_a_SOURCES = \ + condition.cxx \ + props.cxx \ + props_io.cxx + +noinst_PROGRAMS = props_test + +props_test_SOURCES = props_test.cxx +props_test_LDADD = \ + $(top_builddir)/simgear/props/libsgprops.a \ + $(top_builddir)/simgear/xml/libsgxml.a \ + $(top_builddir)/simgear/misc/libsgmisc.a \ + $(top_builddir)/simgear/debug/libsgdebug.a + +INCLUDES = -I$(top_srcdir) diff --git a/simgear/props/condition.cxx b/simgear/props/condition.cxx new file mode 100644 index 00000000..d50f461a --- /dev/null +++ b/simgear/props/condition.cxx @@ -0,0 +1,435 @@ +// condition.hxx - Declarations and inline methods for property conditions. +// Written by David Megginson, started 2000. +// CLO May 2003 - Split out condition specific code. +// +// This file is in the Public Domain, and comes with no warranty. +// +// $Id$ + +#ifdef HAVE_CONFIG_H +# include +#endif + +// #include STL_IOSTREAM + +#include + +#include "props.hxx" + +#include "condition.hxx" + +SG_USING_STD(istream); +SG_USING_STD(ostream); + + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGCondition. +//////////////////////////////////////////////////////////////////////// + +FGCondition::FGCondition () +{ +} + +FGCondition::~FGCondition () +{ +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGPropertyCondition. +//////////////////////////////////////////////////////////////////////// + +FGPropertyCondition::FGPropertyCondition ( SGPropertyNode *prop_root, + const char *propname ) + : _node( prop_root->getNode(propname, true) ) +{ + cout << "FGPropertyCondition::FGPropertyCondition()" << endl; + cout << " prop_root = " << prop_root << endl; + cout << " propname = " << propname << endl; + _node = prop_root->getNode(propname, true); + cout << " _node = " << _node << endl; +} + +FGPropertyCondition::~FGPropertyCondition () +{ +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGNotCondition. +//////////////////////////////////////////////////////////////////////// + +FGNotCondition::FGNotCondition (FGCondition * condition) + : _condition(condition) +{ +} + +FGNotCondition::~FGNotCondition () +{ + delete _condition; +} + +bool +FGNotCondition::test () const +{ + return !(_condition->test()); +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGAndCondition. +//////////////////////////////////////////////////////////////////////// + +FGAndCondition::FGAndCondition () +{ +} + +FGAndCondition::~FGAndCondition () +{ + for (unsigned int i = 0; i < _conditions.size(); i++) + delete _conditions[i]; +} + +bool +FGAndCondition::test () const +{ + int nConditions = _conditions.size(); + for (int i = 0; i < nConditions; i++) { + if (!_conditions[i]->test()) + return false; + } + return true; +} + +void +FGAndCondition::addCondition (FGCondition * condition) +{ + _conditions.push_back(condition); +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGOrCondition. +//////////////////////////////////////////////////////////////////////// + +FGOrCondition::FGOrCondition () +{ +} + +FGOrCondition::~FGOrCondition () +{ + for (unsigned int i = 0; i < _conditions.size(); i++) + delete _conditions[i]; +} + +bool +FGOrCondition::test () const +{ + int nConditions = _conditions.size(); + for (int i = 0; i < nConditions; i++) { + if (_conditions[i]->test()) + return true; + } + return false; +} + +void +FGOrCondition::addCondition (FGCondition * condition) +{ + _conditions.push_back(condition); +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGComparisonCondition. +//////////////////////////////////////////////////////////////////////// + +static int +doComparison (const SGPropertyNode * left, const SGPropertyNode *right) +{ + switch (left->getType()) { + case SGPropertyNode::BOOL: { + bool v1 = left->getBoolValue(); + bool v2 = right->getBoolValue(); + if (v1 < v2) + return FGComparisonCondition::LESS_THAN; + else if (v1 > v2) + return FGComparisonCondition::GREATER_THAN; + else + return FGComparisonCondition::EQUALS; + break; + } + case SGPropertyNode::INT: { + int v1 = left->getIntValue(); + int v2 = right->getIntValue(); + if (v1 < v2) + return FGComparisonCondition::LESS_THAN; + else if (v1 > v2) + return FGComparisonCondition::GREATER_THAN; + else + return FGComparisonCondition::EQUALS; + break; + } + case SGPropertyNode::LONG: { + long v1 = left->getLongValue(); + long v2 = right->getLongValue(); + if (v1 < v2) + return FGComparisonCondition::LESS_THAN; + else if (v1 > v2) + return FGComparisonCondition::GREATER_THAN; + else + return FGComparisonCondition::EQUALS; + break; + } + case SGPropertyNode::FLOAT: { + float v1 = left->getFloatValue(); + float v2 = right->getFloatValue(); + if (v1 < v2) + return FGComparisonCondition::LESS_THAN; + else if (v1 > v2) + return FGComparisonCondition::GREATER_THAN; + else + return FGComparisonCondition::EQUALS; + break; + } + case SGPropertyNode::DOUBLE: { + double v1 = left->getDoubleValue(); + double v2 = right->getDoubleValue(); + if (v1 < v2) + return FGComparisonCondition::LESS_THAN; + else if (v1 > v2) + return FGComparisonCondition::GREATER_THAN; + else + return FGComparisonCondition::EQUALS; + break; + } + case SGPropertyNode::STRING: + case SGPropertyNode::NONE: + case SGPropertyNode::UNSPECIFIED: { + string v1 = left->getStringValue(); + string v2 = right->getStringValue(); + if (v1 < v2) + return FGComparisonCondition::LESS_THAN; + else if (v1 > v2) + return FGComparisonCondition::GREATER_THAN; + else + return FGComparisonCondition::EQUALS; + break; + } + } + throw sg_exception("Unrecognized node type"); + return 0; +} + + +FGComparisonCondition::FGComparisonCondition (Type type, bool reverse) + : _type(type), + _reverse(reverse), + _left_property(0), + _right_property(0), + _right_value(0) +{ +} + +FGComparisonCondition::~FGComparisonCondition () +{ + delete _right_value; +} + +bool +FGComparisonCondition::test () const +{ + // Always fail if incompletely specified + if (_left_property == 0 || + (_right_property == 0 && _right_value == 0)) + return false; + + // Get LESS_THAN, EQUALS, or GREATER_THAN + int cmp = + doComparison(_left_property, + (_right_property != 0 ? _right_property : _right_value)); + if (!_reverse) + return (cmp == _type); + else + return (cmp != _type); +} + +void +FGComparisonCondition::setLeftProperty( SGPropertyNode *prop_root, + const char * propname ) +{ + _left_property = prop_root->getNode(propname, true); +} + +void +FGComparisonCondition::setRightProperty( SGPropertyNode *prop_root, + const char * propname ) +{ + delete _right_value; + _right_value = 0; + _right_property = prop_root->getNode(propname, true); +} + +void +FGComparisonCondition::setRightValue (const SGPropertyNode *node) +{ + _right_property = 0; + delete _right_value; + _right_value = new SGPropertyNode(*node); +} + + + +//////////////////////////////////////////////////////////////////////// +// Read a condition and use it if necessary. +//////////////////////////////////////////////////////////////////////// + + // Forward declaration +static FGCondition * readCondition( SGPropertyNode *prop_root, + const SGPropertyNode *node ); + +static FGCondition * +readPropertyCondition( SGPropertyNode *prop_root, + const SGPropertyNode *node ) +{ + return new FGPropertyCondition( prop_root, node->getStringValue() ); +} + +static FGCondition * +readNotCondition( SGPropertyNode *prop_root, const SGPropertyNode *node ) +{ + int nChildren = node->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode * child = node->getChild(i); + FGCondition * condition = readCondition(prop_root, child); + if (condition != 0) + return new FGNotCondition(condition); + } + SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition"); + return 0; +} + +static FGCondition * +readAndConditions( SGPropertyNode *prop_root, const SGPropertyNode *node ) +{ + FGAndCondition * andCondition = new FGAndCondition; + int nChildren = node->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode * child = node->getChild(i); + FGCondition * condition = readCondition(prop_root, child); + if (condition != 0) + andCondition->addCondition(condition); + } + return andCondition; +} + +static FGCondition * +readOrConditions( SGPropertyNode *prop_root, const SGPropertyNode *node ) +{ + FGOrCondition * orCondition = new FGOrCondition; + int nChildren = node->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode * child = node->getChild(i); + FGCondition * condition = readCondition(prop_root, child); + if (condition != 0) + orCondition->addCondition(condition); + } + return orCondition; +} + +static FGCondition * +readComparison( SGPropertyNode *prop_root, + const SGPropertyNode *node, + FGComparisonCondition::Type type, + bool reverse) +{ + FGComparisonCondition * condition = new FGComparisonCondition(type, reverse); + condition->setLeftProperty(prop_root, node->getStringValue("property[0]")); + if (node->hasValue("property[1]")) + condition->setRightProperty(prop_root, node->getStringValue("property[1]")); + else + condition->setRightValue(node->getChild("value", 0)); + + return condition; +} + +static FGCondition * +readCondition( SGPropertyNode *prop_root, const SGPropertyNode *node ) +{ + const string &name = node->getName(); + if (name == "property") + return readPropertyCondition(prop_root, node); + else if (name == "not") + return readNotCondition(prop_root, node); + else if (name == "and") + return readAndConditions(prop_root, node); + else if (name == "or") + return readOrConditions(prop_root, node); + else if (name == "less-than") + return readComparison(prop_root, node, FGComparisonCondition::LESS_THAN, + false); + else if (name == "less-than-equals") + return readComparison(prop_root, node, FGComparisonCondition::GREATER_THAN, + true); + else if (name == "greater-than") + return readComparison(prop_root, node, FGComparisonCondition::GREATER_THAN, + false); + else if (name == "greater-than-equals") + return readComparison(prop_root, node, FGComparisonCondition::LESS_THAN, + true); + else if (name == "equals") + return readComparison(prop_root, node, FGComparisonCondition::EQUALS, + false); + else if (name == "not-equals") + return readComparison(prop_root, node, FGComparisonCondition::EQUALS, true); + else + return 0; +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGConditional. +//////////////////////////////////////////////////////////////////////// + +FGConditional::FGConditional () + : _condition (0) +{ +} + +FGConditional::~FGConditional () +{ + delete _condition; +} + +void +FGConditional::setCondition (FGCondition * condition) +{ + delete _condition; + _condition = condition; +} + +bool +FGConditional::test () const +{ + return ((_condition == 0) || _condition->test()); +} + + + +// The top-level is always an implicit 'and' group +FGCondition * +fgReadCondition( SGPropertyNode *prop_root, const SGPropertyNode *node ) +{ + return readAndConditions(prop_root, node); +} + + +// end of fg_props.cxx diff --git a/simgear/props/condition.hxx b/simgear/props/condition.hxx new file mode 100644 index 00000000..3da63712 --- /dev/null +++ b/simgear/props/condition.hxx @@ -0,0 +1,176 @@ +// condition.hxx - Declarations and inline methods for property conditions. +// Written by David Megginson, started 2000. +// CLO May 2003 - Split out condition specific code. +// +// This file is in the Public Domain, and comes with no warranty. + +#ifndef __SG_CONDITION_HXX +#define __SG_CONDITION_HXX + +#include +#include +#include + + +//////////////////////////////////////////////////////////////////////// +// Conditions. +//////////////////////////////////////////////////////////////////////// + + +/** + * An encoded condition. + * + * This class encodes a single condition of some sort, possibly + * connected with properties. + * + * This class should migrate to somewhere more general. + */ +class FGCondition +{ +public: + FGCondition (); + virtual ~FGCondition (); + virtual bool test () const = 0; +}; + + +/** + * Condition for a single property. + * + * This condition is true only if the property returns a boolean + * true value. + */ +class FGPropertyCondition : public FGCondition +{ +public: + FGPropertyCondition ( SGPropertyNode *prop_root, + const char * propname ); + virtual ~FGPropertyCondition (); + virtual bool test () const { return _node->getBoolValue(); } +private: + const SGPropertyNode * _node; +}; + + +/** + * Condition for a 'not' operator. + * + * This condition is true only if the child condition is false. + */ +class FGNotCondition : public FGCondition +{ +public: + // transfer pointer ownership + FGNotCondition (FGCondition * condition); + virtual ~FGNotCondition (); + virtual bool test () const; +private: + FGCondition * _condition; +}; + + +/** + * Condition for an 'and' group. + * + * This condition is true only if all of the conditions + * in the group are true. + */ +class FGAndCondition : public FGCondition +{ +public: + FGAndCondition (); + virtual ~FGAndCondition (); + virtual bool test () const; + // transfer pointer ownership + virtual void addCondition (FGCondition * condition); +private: + vector _conditions; +}; + + +/** + * Condition for an 'or' group. + * + * This condition is true if at least one of the conditions in the + * group is true. + */ +class FGOrCondition : public FGCondition +{ +public: + FGOrCondition (); + virtual ~FGOrCondition (); + virtual bool test () const; + // transfer pointer ownership + virtual void addCondition (FGCondition * condition); +private: + vector _conditions; +}; + + +/** + * Abstract base class for property comparison conditions. + */ +class FGComparisonCondition : public FGCondition +{ +public: + enum Type { + LESS_THAN, + GREATER_THAN, + EQUALS + }; + FGComparisonCondition (Type type, bool reverse = false); + virtual ~FGComparisonCondition (); + virtual bool test () const; + virtual void setLeftProperty( SGPropertyNode *prop_root, + const char * propname ); + virtual void setRightProperty( SGPropertyNode *prop_root, + const char * propname ); + // will make a local copy + virtual void setRightValue (const SGPropertyNode * value); +private: + Type _type; + bool _reverse; + const SGPropertyNode * _left_property; + const SGPropertyNode * _right_property; + const SGPropertyNode * _right_value; +}; + + +/** + * Base class for a conditional components. + * + * This class manages the conditions and tests; the component should + * invoke the test() method whenever it needs to decide whether to + * active itself, draw itself, and so on. + */ +class FGConditional +{ +public: + FGConditional (); + virtual ~FGConditional (); + // transfer pointer ownership + virtual void setCondition (FGCondition * condition); + virtual const FGCondition * getCondition () const { return _condition; } + virtual bool test () const; +private: + FGCondition * _condition; +}; + + +/** + * Global function to make a condition out of properties. + * + * The top-level is always an implicit 'and' group, whatever the + * node's name (it should usually be "condition"). + * + * @param node The top-level condition node (usually named "condition"). + * @return A pointer to a newly-allocated condition; it is the + * responsibility of the caller to delete the condition when + * it is no longer needed. + */ +FGCondition * fgReadCondition( SGPropertyNode *prop_root, + const SGPropertyNode *node ); + + +#endif // __SG_CONDITION_HXX + diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx new file mode 100644 index 00000000..790aa05c --- /dev/null +++ b/simgear/props/props.cxx @@ -0,0 +1,2374 @@ +// props.cxx - implementation of a property list. +// Started Fall 2000 by David Megginson, david@megginson.com +// This code is released into the Public Domain. +// +// See props.html for documentation [replace with URL when available]. +// +// $Id$ + +#include "props.hxx" + +#include +#include +#include + +#if PROPS_STANDALONE + +#include +using std::cerr; +using std::endl; +using std::find; +using std::sort; +using std::vector; + +#else + +#include +#include + +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); +SG_USING_STD(vector); +SG_USING_STD(vector); +#endif + +#endif + + + +//////////////////////////////////////////////////////////////////////// +// Local classes. +//////////////////////////////////////////////////////////////////////// + +/** + * Comparator class for sorting by index. + */ +class CompareIndices +{ +public: + int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const { + return (n1->getIndex() < n2->getIndex()); + } +}; + + + +//////////////////////////////////////////////////////////////////////// +// Convenience macros for value access. +//////////////////////////////////////////////////////////////////////// + +#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt +#define TEST_WRITE if (!getAttribute(WRITE)) return false + + + +//////////////////////////////////////////////////////////////////////// +// Default values for every type. +//////////////////////////////////////////////////////////////////////// + +const bool SGRawValue::DefaultValue = false; +const int SGRawValue::DefaultValue = 0; +const long SGRawValue::DefaultValue = 0L; +const float SGRawValue::DefaultValue = 0.0; +const double SGRawValue::DefaultValue = 0.0L; +const char * const SGRawValue::DefaultValue = ""; + + + +//////////////////////////////////////////////////////////////////////// +// Local path normalization code. +//////////////////////////////////////////////////////////////////////// + +/** + * A component in a path. + */ +struct PathComponent +{ + string name; + int index; +}; + +/** + * Parse the name for a path component. + * + * Name: [_a-zA-Z][-._a-zA-Z0-9]* + */ +static inline const string +parse_name (const string &path, int &i) +{ + string name = ""; + int max = path.size(); + + if (path[i] == '.') { + i++; + if (i < max && path[i] == '.') { + i++; + name = ".."; + } else { + name = "."; + } + if (i < max && path[i] != '/') + throw string(string("Illegal character after ") + name); + } + + else if (isalpha(path[i]) || path[i] == '_') { + name += path[i]; + i++; + + // The rules inside a name are a little + // less restrictive. + while (i < max) { + if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' || + path[i] == '-' || path[i] == '.') { + name += path[i]; + } else if (path[i] == '[' || path[i] == '/') { + break; + } else { + throw string("name may contain only ._- and alphanumeric characters"); + } + i++; + } + } + + else { + if (name.size() == 0) + throw string("name must begin with alpha or '_'"); + } + + return name; +} + + +/** + * Parse the optional integer index for a path component. + * + * Index: "[" [0-9]+ "]" + */ +static inline int +parse_index (const string &path, int &i) +{ + int index = 0; + + if (path[i] != '[') + return 0; + else + i++; + + for (int max = path.size(); i < max; i++) { + if (isdigit(path[i])) { + index = (index * 10) + (path[i] - '0'); + } else if (path[i] == ']') { + i++; + return index; + } else { + break; + } + } + + throw string("unterminated index (looking for ']')"); +} + + +/** + * Parse a single path component. + * + * Component: Name Index? + */ +static inline PathComponent +parse_component (const string &path, int &i) +{ + PathComponent component; + component.name = parse_name(path, i); + if (component.name[0] != '.') + component.index = parse_index(path, i); + else + component.index = -1; + return component; +} + + +/** + * Parse a path into its components. + */ +static void +parse_path (const string &path, vector &components) +{ + int pos = 0; + int max = path.size(); + + // Check for initial '/' + if (path[pos] == '/') { + PathComponent root; + root.name = ""; + root.index = -1; + components.push_back(root); + pos++; + while (pos < max && path[pos] == '/') + pos++; + } + + while (pos < max) { + components.push_back(parse_component(path, pos)); + while (pos < max && path[pos] == '/') + pos++; + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Other static utility functions. +//////////////////////////////////////////////////////////////////////// + + +static char * +copy_string (const char * s) +{ + // FIXME: potential buffer overflow. + // For some reason, strnlen and + // strncpy cause all kinds of crashes. + char * copy = new char[strlen(s) + 1]; + strcpy(copy, s); + return copy; +} + +static bool +compare_strings (const char * s1, const char * s2) +{ + return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN); +} + +/** + * Locate a child node by name and index. + */ +static int +find_child (const char * name, int index, vector nodes) +{ + int nNodes = nodes.size(); + for (int i = 0; i < nNodes; i++) { + SGPropertyNode * node = nodes[i]; + if (compare_strings(node->getName(), name) && node->getIndex() == index) + return i; + } + return -1; +} + + +/** + * Locate another node, given a relative path. + */ +static SGPropertyNode * +find_node (SGPropertyNode * current, + const vector &components, + int position, + bool create) +{ + // Run off the end of the list + if (current == 0) { + return 0; + } + + // Success! This is the one we want. + else if (position >= (int)components.size()) { + return current; + } + + // Empty component means root. + else if (components[position].name == "") { + return find_node(current->getRootNode(), components, position + 1, create); + } + + // . means current directory + else if (components[position].name == ".") { + return find_node(current, components, position + 1, create); + } + + // .. means parent directory + else if (components[position].name == "..") { + SGPropertyNode * parent = current->getParent(); + if (parent == 0) + throw string("Attempt to move past root with '..'"); + else + return find_node(parent, components, position + 1, create); + } + + // Otherwise, a child name + else { + SGPropertyNode * child = + current->getChild(components[position].name.c_str(), + components[position].index, + create); + return find_node(child, components, position + 1, create); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Private methods from SGPropertyNode (may be inlined for speed). +//////////////////////////////////////////////////////////////////////// + +inline bool +SGPropertyNode::get_bool () const +{ + if (_tied) + return _value.bool_val->getValue(); + else + return _local_val.bool_val; +} + +inline int +SGPropertyNode::get_int () const +{ + if (_tied) + return _value.int_val->getValue(); + else + return _local_val.int_val; +} + +inline long +SGPropertyNode::get_long () const +{ + if (_tied) + return _value.long_val->getValue(); + else + return _local_val.long_val; +} + +inline float +SGPropertyNode::get_float () const +{ + if (_tied) + return _value.float_val->getValue(); + else + return _local_val.float_val; +} + +inline double +SGPropertyNode::get_double () const +{ + if (_tied) + return _value.double_val->getValue(); + else + return _local_val.double_val; +} + +inline const char * +SGPropertyNode::get_string () const +{ + if (_tied) + return _value.string_val->getValue(); + else + return _local_val.string_val; +} + +inline bool +SGPropertyNode::set_bool (bool val) +{ + if (_tied) { + if (_value.bool_val->setValue(val)) { + fireValueChanged(); + return true; + } else { + return false; + } + } else { + _local_val.bool_val = val; + fireValueChanged(); + return true; + } +} + +inline bool +SGPropertyNode::set_int (int val) +{ + if (_tied) { + if (_value.int_val->setValue(val)) { + fireValueChanged(); + return true; + } else { + return false; + } + } else { + _local_val.int_val = val; + fireValueChanged(); + return true; + } +} + +inline bool +SGPropertyNode::set_long (long val) +{ + if (_tied) { + if (_value.long_val->setValue(val)) { + fireValueChanged(); + return true; + } else { + return false; + } + } else { + _local_val.long_val = val; + fireValueChanged(); + return true; + } +} + +inline bool +SGPropertyNode::set_float (float val) +{ + if (_tied) { + if (_value.float_val->setValue(val)) { + fireValueChanged(); + return true; + } else { + return false; + } + } else { + _local_val.float_val = val; + fireValueChanged(); + return true; + } +} + +inline bool +SGPropertyNode::set_double (double val) +{ + if (_tied) { + if (_value.double_val->setValue(val)) { + fireValueChanged(); + return true; + } else { + return false; + } + } else { + _local_val.double_val = val; + fireValueChanged(); + return true; + } +} + +inline bool +SGPropertyNode::set_string (const char * val) +{ + if (_tied) { + if (_value.string_val->setValue(val)) { + fireValueChanged(); + return true; + } else { + return false; + } + } else { + delete [] _local_val.string_val; + _local_val.string_val = copy_string(val); + fireValueChanged(); + return true; + } +} + +void +SGPropertyNode::clear_value () +{ + switch (_type) { + case NONE: + break; + case ALIAS: + _value.alias = 0; + break; + case BOOL: + if (_tied) { + delete _value.bool_val; + _value.bool_val = 0; + } + _local_val.bool_val = SGRawValue::DefaultValue; + break; + case INT: + if (_tied) { + delete _value.int_val; + _value.int_val = 0; + } + _local_val.int_val = SGRawValue::DefaultValue; + break; + case LONG: + if (_tied) { + delete _value.long_val; + _value.long_val = 0L; + } + _local_val.long_val = SGRawValue::DefaultValue; + break; + case FLOAT: + if (_tied) { + delete _value.float_val; + _value.float_val = 0; + } + _local_val.float_val = SGRawValue::DefaultValue; + break; + case DOUBLE: + if (_tied) { + delete _value.double_val; + _value.double_val = 0; + } + _local_val.double_val = SGRawValue::DefaultValue; + break; + case STRING: + case UNSPECIFIED: + if (_tied) { + delete _value.string_val; + _value.string_val = 0; + } else { + delete [] _local_val.string_val; + } + _local_val.string_val = 0; + break; + } + _tied = false; + _type = NONE; +} + + +/** + * Get the value as a string. + */ +const char * +SGPropertyNode::make_string () const +{ + if (!getAttribute(READ)) + return ""; + + switch (_type) { + case ALIAS: + return _value.alias->getStringValue(); + case BOOL: + if (get_bool()) + return "true"; + else + return "false"; + case INT: + sprintf(_buffer, "%d", get_int()); + return _buffer; + case LONG: + sprintf(_buffer, "%ld", get_long()); + return _buffer; + case FLOAT: + sprintf(_buffer, "%f", get_float()); + return _buffer; + case DOUBLE: + sprintf(_buffer, "%f", get_double()); + return _buffer; + case STRING: + case UNSPECIFIED: + return get_string(); + case NONE: + default: + return ""; + } +} + +/** + * Trace a write access for a property. + */ +void +SGPropertyNode::trace_write () const +{ +#if PROPS_STANDALONE + cerr << "TRACE: Write node " << getPath () << ", value\"" + << make_string() << '"' << endl; +#else + SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath() + << ", value\"" << make_string() << '"'); +#endif +} + +/** + * Trace a read access for a property. + */ +void +SGPropertyNode::trace_read () const +{ +#if PROPS_STANDALONE + cerr << "TRACE: Write node " << getPath () << ", value \"" + << make_string() << '"' << endl; +#else + SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath() + << ", value \"" << make_string() << '"'); +#endif +} + +/** + * Increment reference counter + */ +void +SGPropertyNode::incrementRef() +{ + ++_count; +} + +/** + * Decrement reference counter + */ +int +SGPropertyNode::decrementRef() +{ + return --_count; +} + + + +//////////////////////////////////////////////////////////////////////// +// 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), + _count(0), + _listeners(0) +{ + _local_val.string_val = 0; +} + + +/** + * Copy constructor. + */ +SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) + : _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), + _count(0), + _listeners(0) // CHECK!! +{ + _name = copy_string(node._name); + _local_val.string_val = 0; + switch (_type) { + case NONE: + break; + case ALIAS: + _value.alias = node._value.alias; + _tied = false; + break; + case BOOL: + if (_tied) { + _tied = true; + _value.bool_val = node._value.bool_val->clone(); + } else { + _tied = false; + set_bool(node.get_bool()); + } + break; + case INT: + if (_tied) { + _tied = true; + _value.int_val = node._value.int_val->clone(); + } else { + _tied = false; + set_int(node.get_int()); + } + break; + case LONG: + if (_tied) { + _tied = true; + _value.long_val = node._value.long_val->clone(); + } else { + _tied = false; + set_long(node.get_long()); + } + break; + case FLOAT: + if (_tied) { + _tied = true; + _value.float_val = node._value.float_val->clone(); + } else { + _tied = false; + set_float(node.get_float()); + } + break; + case DOUBLE: + if (_tied) { + _tied = true; + _value.double_val = node._value.double_val->clone(); + } else { + _tied = false; + set_double(node.get_double()); + } + break; + case STRING: + case UNSPECIFIED: + if (_tied) { + _tied = true; + _value.string_val = node._value.string_val->clone(); + } else { + _tied = false; + set_string(node.get_string()); + } + break; + } +} + + +/** + * Convenience constructor. + */ +SGPropertyNode::SGPropertyNode (const char * name, + int index, + SGPropertyNode * parent) + : _display_name(0), + _index(index), + _parent(parent), + _path(0), + _path_cache(0), + _type(NONE), + _tied(false), + _attr(READ|WRITE), + _count(0), + _listeners(0) +{ + _name = copy_string(name); + _local_val.string_val = 0; +} + + +/** + * Destructor. + */ +SGPropertyNode::~SGPropertyNode () +{ + delete [] _name; + delete [] _display_name; + delete [] _path; + delete _path_cache; + clear_value(); + delete _listeners; +} + + +/** + * Alias to another node. + */ +bool +SGPropertyNode::alias (SGPropertyNode * target) +{ + if (target == 0 || _type == ALIAS || _tied) + return false; + clear_value(); + _value.alias = target; + _type = ALIAS; + return true; +} + + +/** + * Alias to another node by path. + */ +bool +SGPropertyNode::alias (const char * path) +{ + return alias(getNode(path, true)); +} + + +/** + * Remove an alias. + */ +bool +SGPropertyNode::unalias () +{ + if (_type != ALIAS) + return false; + _type = NONE; + _value.alias = 0; + return true; +} + + +/** + * Get the target of an alias. + */ +SGPropertyNode * +SGPropertyNode::getAliasTarget () +{ + return (_type == ALIAS ? _value.alias : 0); +} + + +const SGPropertyNode * +SGPropertyNode::getAliasTarget () const +{ + return (_type == ALIAS ? _value.alias : 0); +} + + +/** + * Get a non-const child by index. + */ +SGPropertyNode * +SGPropertyNode::getChild (int position) +{ + if (position >= 0 && position < nChildren()) + return _children[position]; + else + return 0; +} + + +/** + * Get a const child by index. + */ +const SGPropertyNode * +SGPropertyNode::getChild (int position) const +{ + if (position >= 0 && position < nChildren()) + return _children[position]; + else + return 0; +} + + +/** + * Get a non-const child by name and index, creating if necessary. + */ +SGPropertyNode * +SGPropertyNode::getChild (const char * name, int index, bool create) +{ + int pos = find_child(name, index, _children); + if (pos >= 0) { + return _children[pos]; + } else if (create) { + SGPropertyNode_ptr node; + pos = find_child(name, index, _removedChildren); + if (pos >= 0) { + vector::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; + } +} + + +/** + * Get a const child by name and index. + */ +const SGPropertyNode * +SGPropertyNode::getChild (const char * name, int index) const +{ + int pos = find_child(name, index, _children); + if (pos >= 0) + return _children[pos]; + else + return 0; +} + + +/** + * Get all children with the same name (but different indices). + */ +vector +SGPropertyNode::getChildren (const char * name) const +{ + vector children; + int max = _children.size(); + + 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; +} + + +/** + * Remove a child node + */ +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::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; +} + + +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 +{ + // 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; +} + +SGPropertyNode::Type +SGPropertyNode::getType () const +{ + if (_type == ALIAS) + return _value.alias->getType(); + else + return _type; +} + + +bool +SGPropertyNode::getBoolValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == BOOL) + return get_bool(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + switch (_type) { + case ALIAS: + return _value.alias->getBoolValue(); + case BOOL: + return get_bool(); + case INT: + return get_int() == 0 ? false : true; + case LONG: + return get_long() == 0L ? false : true; + case FLOAT: + return get_float() == 0.0 ? false : true; + case DOUBLE: + return get_double() == 0.0L ? false : true; + case STRING: + case UNSPECIFIED: + return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L); + case NONE: + default: + return SGRawValue::DefaultValue; + } +} + +int +SGPropertyNode::getIntValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == INT) + return get_int(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + switch (_type) { + case ALIAS: + return _value.alias->getIntValue(); + case BOOL: + return int(get_bool()); + case INT: + return get_int(); + case LONG: + return int(get_long()); + case FLOAT: + return int(get_float()); + case DOUBLE: + return int(get_double()); + case STRING: + case UNSPECIFIED: + return atoi(get_string()); + case NONE: + default: + return SGRawValue::DefaultValue; + } +} + +long +SGPropertyNode::getLongValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == LONG) + return get_long(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + switch (_type) { + case ALIAS: + return _value.alias->getLongValue(); + case BOOL: + return long(get_bool()); + case INT: + return long(get_int()); + case LONG: + return get_long(); + case FLOAT: + return long(get_float()); + case DOUBLE: + return long(get_double()); + case STRING: + case UNSPECIFIED: + return strtol(get_string(), 0, 0); + case NONE: + default: + return SGRawValue::DefaultValue; + } +} + +float +SGPropertyNode::getFloatValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == FLOAT) + return get_float(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + switch (_type) { + case ALIAS: + return _value.alias->getFloatValue(); + case BOOL: + return float(get_bool()); + case INT: + return float(get_int()); + case LONG: + return float(get_long()); + case FLOAT: + return get_float(); + case DOUBLE: + return float(get_double()); + case STRING: + case UNSPECIFIED: + return atof(get_string()); + case NONE: + default: + return SGRawValue::DefaultValue; + } +} + +double +SGPropertyNode::getDoubleValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == DOUBLE) + return get_double(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + + switch (_type) { + case ALIAS: + return _value.alias->getDoubleValue(); + case BOOL: + return double(get_bool()); + case INT: + return double(get_int()); + case LONG: + return double(get_long()); + case FLOAT: + return double(get_float()); + case DOUBLE: + return get_double(); + case STRING: + case UNSPECIFIED: + return strtod(get_string(), 0); + case NONE: + default: + return SGRawValue::DefaultValue; + } +} + +const char * +SGPropertyNode::getStringValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == STRING) + return get_string(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + return make_string(); +} + +bool +SGPropertyNode::setBoolValue (bool value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == BOOL) + return set_bool(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _tied = false; + _type = BOOL; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setBoolValue(value); + break; + case BOOL: + result = set_bool(value); + break; + case INT: + result = set_int(int(value)); + break; + case LONG: + result = set_long(long(value)); + break; + case FLOAT: + result = set_float(float(value)); + break; + case DOUBLE: + result = set_double(double(value)); + break; + case STRING: + case UNSPECIFIED: + result = set_string(value ? "true" : "false"); + break; + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::setIntValue (int value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == INT) + return set_int(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _type = INT; + _local_val.int_val = 0; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setIntValue(value); + break; + case BOOL: + result = set_bool(value == 0 ? false : true); + break; + case INT: + result = set_int(value); + break; + case LONG: + result = set_long(long(value)); + break; + case FLOAT: + result = set_float(float(value)); + break; + case DOUBLE: + result = set_double(double(value)); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%d", value); + result = set_string(buf); + break; + } + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::setLongValue (long value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == LONG) + return set_long(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _type = LONG; + _local_val.long_val = 0L; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setLongValue(value); + break; + case BOOL: + result = set_bool(value == 0L ? false : true); + break; + case INT: + result = set_int(int(value)); + break; + case LONG: + result = set_long(value); + break; + case FLOAT: + result = set_float(float(value)); + break; + case DOUBLE: + result = set_double(double(value)); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%ld", value); + result = set_string(buf); + break; + } + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::setFloatValue (float value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == FLOAT) + return set_float(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _type = FLOAT; + _local_val.float_val = 0; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setFloatValue(value); + break; + case BOOL: + result = set_bool(value == 0.0 ? false : true); + break; + case INT: + result = set_int(int(value)); + break; + case LONG: + result = set_long(long(value)); + break; + case FLOAT: + result = set_float(value); + break; + case DOUBLE: + result = set_double(double(value)); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%f", value); + result = set_string(buf); + break; + } + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::setDoubleValue (double value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == DOUBLE) + return set_double(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _local_val.double_val = value; + _type = DOUBLE; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setDoubleValue(value); + break; + case BOOL: + result = set_bool(value == 0.0L ? false : true); + break; + case INT: + result = set_int(int(value)); + break; + case LONG: + result = set_long(long(value)); + break; + case FLOAT: + result = set_float(float(value)); + break; + case DOUBLE: + result = set_double(value); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%f", value); + result = set_string(buf); + break; + } + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::setStringValue (const char * value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == STRING) + return set_string(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _type = STRING; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setStringValue(value); + break; + case BOOL: + result = set_bool((compare_strings(value, "true") + || atoi(value)) ? true : false); + break; + case INT: + result = set_int(atoi(value)); + break; + case LONG: + result = set_long(strtol(value, 0, 0)); + break; + case FLOAT: + result = set_float(atof(value)); + break; + case DOUBLE: + result = set_double(strtod(value, 0)); + break; + case STRING: + case UNSPECIFIED: + result = set_string(value); + break; + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::setUnspecifiedValue (const char * value) +{ + bool result = false; + TEST_WRITE; + if (_type == NONE) { + clear_value(); + _type = UNSPECIFIED; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setUnspecifiedValue(value); + break; + case BOOL: + result = set_bool((compare_strings(value, "true") + || atoi(value)) ? true : false); + break; + case INT: + result = set_int(atoi(value)); + break; + case LONG: + result = set_long(strtol(value, 0, 0)); + break; + case FLOAT: + result = set_float(atof(value)); + break; + case DOUBLE: + result = set_double(strtod(value, 0)); + break; + case STRING: + case UNSPECIFIED: + result = set_string(value); + break; + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} + +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + bool old_val = false; + if (useDefault) + old_val = getBoolValue(); + + clear_value(); + _type = BOOL; + _tied = true; + _value.bool_val = rawValue.clone(); + + if (useDefault) + setBoolValue(old_val); + + return true; +} + +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + int old_val = 0; + if (useDefault) + old_val = getIntValue(); + + clear_value(); + _type = INT; + _tied = true; + _value.int_val = rawValue.clone(); + + if (useDefault) + setIntValue(old_val); + + return true; +} + +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + long old_val = 0; + if (useDefault) + old_val = getLongValue(); + + clear_value(); + _type = LONG; + _tied = true; + _value.long_val = rawValue.clone(); + + if (useDefault) + setLongValue(old_val); + + return true; +} + +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + float old_val = 0.0; + if (useDefault) + old_val = getFloatValue(); + + clear_value(); + _type = FLOAT; + _tied = true; + _value.float_val = rawValue.clone(); + + if (useDefault) + setFloatValue(old_val); + + return true; +} + +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + double old_val = 0.0; + if (useDefault) + old_val = getDoubleValue(); + + clear_value(); + _type = DOUBLE; + _tied = true; + _value.double_val = rawValue.clone(); + + if (useDefault) + setDoubleValue(old_val); + + return true; + +} + +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + string old_val; + if (useDefault) + old_val = getStringValue(); + + clear_value(); + _type = STRING; + _tied = true; + _value.string_val = rawValue.clone(); + + if (useDefault) + setStringValue(old_val.c_str()); + + return true; +} + +bool +SGPropertyNode::untie () +{ + if (!_tied) + return false; + + switch (_type) { + case BOOL: { + bool val = getBoolValue(); + clear_value(); + _type = BOOL; + _local_val.bool_val = val; + break; + } + case INT: { + int val = getIntValue(); + clear_value(); + _type = INT; + _local_val.int_val = val; + break; + } + case LONG: { + long val = getLongValue(); + clear_value(); + _type = LONG; + _local_val.long_val = val; + break; + } + case FLOAT: { + float val = getFloatValue(); + clear_value(); + _type = FLOAT; + _local_val.float_val = val; + break; + } + case DOUBLE: { + double val = getDoubleValue(); + clear_value(); + _type = DOUBLE; + _local_val.double_val = val; + break; + } + case STRING: + case UNSPECIFIED: { + string val = getStringValue(); + clear_value(); + _type = STRING; + _local_val.string_val = copy_string(val.c_str()); + break; + } + case NONE: + default: + break; + } + + _tied = false; + return true; +} + +SGPropertyNode * +SGPropertyNode::getRootNode () +{ + if (_parent == 0) + return this; + else + return _parent->getRootNode(); +} + +const SGPropertyNode * +SGPropertyNode::getRootNode () const +{ + if (_parent == 0) + return this; + else + return _parent->getRootNode(); +} + +SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path, bool create) +{ + if (_path_cache == 0) + _path_cache = new hash_table; + + SGPropertyNode * result = _path_cache->get(relative_path); + if (result == 0) { + vector components; + parse_path(relative_path, components); + result = find_node(this, components, 0, create); + if (result != 0) + _path_cache->put(relative_path, result); + } + + return result; +} + +SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path, int index, bool create) +{ + vector components; + parse_path(relative_path, components); + if (components.size() > 0) + components[components.size()-1].index = index; + return find_node(this, components, 0, create); +} + +const SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path) const +{ + return ((SGPropertyNode *)this)->getNode(relative_path, false); +} + +const SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path, int index) const +{ + return ((SGPropertyNode *)this)->getNode(relative_path, index, false); +} + + +//////////////////////////////////////////////////////////////////////// +// Convenience methods using relative paths. +//////////////////////////////////////////////////////////////////////// + + +/** + * Test whether another node has a value attached. + */ +bool +SGPropertyNode::hasValue (const char * relative_path) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->hasValue()); +} + + +/** + * Get the value type for another node. + */ +SGPropertyNode::Type +SGPropertyNode::getType (const char * relative_path) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? UNSPECIFIED : (Type)(node->getType())); +} + + +/** + * Get a bool value for another node. + */ +bool +SGPropertyNode::getBoolValue (const char * relative_path, + bool defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getBoolValue()); +} + + +/** + * Get an int value for another node. + */ +int +SGPropertyNode::getIntValue (const char * relative_path, + int defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getIntValue()); +} + + +/** + * Get a long value for another node. + */ +long +SGPropertyNode::getLongValue (const char * relative_path, + long defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getLongValue()); +} + + +/** + * Get a float value for another node. + */ +float +SGPropertyNode::getFloatValue (const char * relative_path, + float defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getFloatValue()); +} + + +/** + * Get a double value for another node. + */ +double +SGPropertyNode::getDoubleValue (const char * relative_path, + double defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getDoubleValue()); +} + + +/** + * Get a string value for another node. + */ +const char * +SGPropertyNode::getStringValue (const char * relative_path, + const char * defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getStringValue()); +} + + +/** + * Set a bool value for another node. + */ +bool +SGPropertyNode::setBoolValue (const char * relative_path, bool value) +{ + return getNode(relative_path, true)->setBoolValue(value); +} + + +/** + * Set an int value for another node. + */ +bool +SGPropertyNode::setIntValue (const char * relative_path, int value) +{ + return getNode(relative_path, true)->setIntValue(value); +} + + +/** + * Set a long value for another node. + */ +bool +SGPropertyNode::setLongValue (const char * relative_path, long value) +{ + return getNode(relative_path, true)->setLongValue(value); +} + + +/** + * Set a float value for another node. + */ +bool +SGPropertyNode::setFloatValue (const char * relative_path, float value) +{ + return getNode(relative_path, true)->setFloatValue(value); +} + + +/** + * Set a double value for another node. + */ +bool +SGPropertyNode::setDoubleValue (const char * relative_path, double value) +{ + return getNode(relative_path, true)->setDoubleValue(value); +} + + +/** + * Set a string value for another node. + */ +bool +SGPropertyNode::setStringValue (const char * relative_path, const char * value) +{ + return getNode(relative_path, true)->setStringValue(value); +} + + +/** + * Set an unknown value for another node. + */ +bool +SGPropertyNode::setUnspecifiedValue (const char * relative_path, + const char * value) +{ + return getNode(relative_path, true)->setUnspecifiedValue(value); +} + + +/** + * Test whether another node is tied. + */ +bool +SGPropertyNode::isTied (const char * relative_path) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->isTied()); +} + + +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + +/** + * Attempt to untie another node reached by a relative path. + */ +bool +SGPropertyNode::untie (const char * relative_path) +{ + SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->untie()); +} + +void +SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener) +{ + if (_listeners == 0) + _listeners = new vector; + _listeners->push_back(listener); + listener->register_property(this); +} + +void +SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener) +{ + vector::iterator it = + find(_listeners->begin(), _listeners->end(), listener); + if (it != _listeners->end()) { + _listeners->erase(it); + listener->unregister_property(this); + if (_listeners->empty()) { + vector * 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); +} + + + +//////////////////////////////////////////////////////////////////////// +// 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; +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of SGPropertyChangeListener. +//////////////////////////////////////////////////////////////////////// + +SGPropertyChangeListener::~SGPropertyChangeListener () +{ + // This will come back and remove + // the current item each time. Is + // that OK? + vector::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::iterator it = + find(_properties.begin(), _properties.end(), node); + if (it != _properties.end()) + _properties.erase(it); +} + + +// end of props.cxx diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx new file mode 100644 index 00000000..a042c61d --- /dev/null +++ b/simgear/props/props.hxx @@ -0,0 +1,1337 @@ +/** + * \file props.hxx + * Interface definition for a property list. + * Started Fall 2000 by David Megginson, david@megginson.com + * This code is released into the Public Domain. + * + * See props.html for documentation [replace with URL when available]. + * + * $Id$ + */ + +#ifndef __PROPS_HXX +#define __PROPS_HXX + +#ifndef PROPS_STANDALONE +#define PROPS_STANDALONE 0 +#endif + +#include + +#if PROPS_STANDALONE + +#include +#include + +using std::string; +using std::vector; +using std::istream; +using std::ostream; + +#else + +#include +#include +#include STL_STRING +#include STL_IOSTREAM +SG_USING_STD(string); +SG_USING_STD(vector); +SG_USING_STD(istream); +SG_USING_STD(ostream); + +#endif + + +#ifdef NONE +#pragma warn A sloppy coder has defined NONE as a macro! +#undef NONE +#endif + +#ifdef ALIAS +#pragma warn A sloppy coder has defined ALIAS as a macro! +#undef ALIAS +#endif + +#ifdef UNSPECIFIED +#pragma warn A sloppy coder has defined UNSPECIFIED as a macro! +#undef UNSPECIFIED +#endif + +#ifdef BOOL +#pragma warn A sloppy coder has defined BOOL as a macro! +#undef BOOL +#endif + +#ifdef INT +#pragma warn A sloppy coder has defined INT as a macro! +#undef INT +#endif + +#ifdef LONG +#pragma warn A sloppy coder has defined LONG as a macro! +#undef LONG +#endif + +#ifdef FLOAT +#pragma warn A sloppy coder has defined FLOAT as a macro! +#undef FLOAT +#endif + +#ifdef DOUBLE +#pragma warn A sloppy coder has defined DOUBLE as a macro! +#undef DOUBLE +#endif + +#ifdef STRING +#pragma warn A sloppy coder has defined STRING as a macro! +#undef STRING +#endif + + + +//////////////////////////////////////////////////////////////////////// +// A raw value. +// +// This is the mechanism that information-providing routines can +// use to link their own values to the property manager. Any +// SGValue can be tied to a raw value and then untied again. +// +// Note: we are forced to use inlined methods here to ensure +// that the templates will be instantiated. We're probably taking +// a small performance hit for that. +//////////////////////////////////////////////////////////////////////// + + +/** + * 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.

+ * + *

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 + * frequently during a session as a value is retyped or bound and + * unbound to various data source, but the abstract SGPropertyNode + * layer insulates the application from those changes. The raw value + * contains no facilities for data binding or for type conversion: it + * is simply the abstraction of a primitive data type (or a compound + * data type, in the case of a string).

+ * + *

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.

+ * + *

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

+ * + * @see SGPropertyNode + * @see SGRawValuePointer + * @see SGRawValueFunctions + * @see SGRawValueFunctionsIndexed + * @see SGRawValueMethods + * @see SGRawValueMethodsIndexed + */ +template +class SGRawValue +{ +public: + + /** + * The default underlying value for this type. + * + * Every raw value has a default; the default is false for a + * boolean, 0 for the various numeric values, and "" for a string. + * If additional types of raw values are added in the future, they + * 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. + + + /** + * Constructor. + * + * Use the default value for this type. + */ + SGRawValue () {} + + + /** + * Destructor. + */ + virtual ~SGRawValue () {} + + + /** + * Return the underlying value. + * + * @return The actual value for the property. + * @see #setValue + */ + virtual T getValue () const = 0; + + + /** + * Assign a new underlying value. + * + * If the new value cannot be set (because this is a read-only + * raw value, or because the new value is not acceptable for + * some reason) this method returns false and leaves the original + * value unchanged. + * + * @param value The actual value for the property. + * @return true if the value was set successfully, false otherwise. + * @see #getValue + */ + virtual bool setValue (T value) = 0; + + + /** + * Create a new deep copy of this raw value. + * + * The copy will contain its own version of the underlying value + * as well. + * + * @return A deep copy of the current object. + */ + virtual SGRawValue * clone () const = 0; +}; + + +/** + * A raw value bound to a pointer. + * + * This is the most efficient way to tie an external value, but also + * the most dangerous, because there is no way for the supplier to + * perform bounds checking and derived calculations except by polling + * the variable to see if it has changed. There is no default + * constructor, because this class would be meaningless without a + * pointer. + */ +template +class SGRawValuePointer : public SGRawValue +{ +public: + + /** + * Explicit pointer constructor. + * + * Create a new raw value bound to the value of the variable + * referenced by the pointer. + * + * @param ptr The pointer to the variable to which this raw value + * will be bound. + */ + SGRawValuePointer (T * ptr) : _ptr(ptr) {} + + /** + * Destructor. + */ + virtual ~SGRawValuePointer () {} + + /** + * Get the underlying value. + * + * This method will dereference the pointer and return the + * variable's value. + */ + virtual T getValue () const { return *_ptr; } + + /** + * Set the underlying value. + * + * This method will dereference the pointer and change the + * variable's value. + */ + virtual bool setValue (T value) { *_ptr = value; return true; } + + /** + * Create a copy of this raw value. + * + * The copy will use the same external pointer as the original. + */ + virtual SGRawValue * clone () const { + return new SGRawValuePointer(_ptr); + } + +private: + T * _ptr; +}; + + +/** + * A value managed through static functions. + * + * A read-only value will not have a setter; a write-only value will + * not have a getter. + */ +template +class SGRawValueFunctions : public SGRawValue +{ +public: + + /** + * The template type of a static getter function. + */ + typedef T (*getter_t)(); + + /** + * The template type of a static setter function. + */ + typedef void (*setter_t)(T); + + /** + * Explicit constructor. + * + * Create a new raw value bound to the getter and setter supplied. + * + * @param getter A static function for getting a value, or 0 + * to read-disable the value. + * @param setter A static function for setting a value, or 0 + * to write-disable the value. + */ + SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0) + : _getter(getter), _setter(setter) {} + + /** + * Destructor. + */ + virtual ~SGRawValueFunctions () {} + + /** + * Get the underlying value. + * + * This method will invoke the getter function to get a value. + * If no getter function was supplied, this method will always + * return the default value for the type. + */ + virtual T getValue () const { + if (_getter) return (*_getter)(); + else return SGRawValue::DefaultValue; + } + + /** + * Set the underlying value. + * + * This method will invoke the setter function to change the + * underlying value. If no setter function was supplied, this + * method will return false. + */ + virtual bool setValue (T value) { + if (_setter) { (*_setter)(value); return true; } + else return false; + } + + /** + * Create a copy of this raw value, bound to the same functions. + */ + virtual SGRawValue * clone () const { + return new SGRawValueFunctions(_getter,_setter); + } + +private: + getter_t _getter; + setter_t _setter; +}; + + +/** + * An indexed value bound to static functions. + * + * A read-only value will not have a setter; a write-only value will + * not have a getter. An indexed value is useful for binding one + * of a list of possible values (such as multiple engines for a + * plane). The index is hard-coded at creation time. + * + * @see SGRawValue + */ +template +class SGRawValueFunctionsIndexed : public SGRawValue +{ +public: + typedef T (*getter_t)(int); + typedef void (*setter_t)(int,T); + SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0) + : _index(index), _getter(getter), _setter(setter) {} + virtual ~SGRawValueFunctionsIndexed () {} + virtual T getValue () const { + if (_getter) return (*_getter)(_index); + else return SGRawValue::DefaultValue; + } + virtual bool setValue (T value) { + if (_setter) { (*_setter)(_index, value); return true; } + else return false; + } + virtual SGRawValue * clone () const { + return new SGRawValueFunctionsIndexed(_index, _getter, _setter); + } +private: + int _index; + getter_t _getter; + setter_t _setter; +}; + + +/** + * A value managed through an object and access methods. + * + * A read-only value will not have a setter; a write-only value will + * not have a getter. + */ +template +class SGRawValueMethods : public SGRawValue +{ +public: + typedef T (C::*getter_t)() const; + typedef void (C::*setter_t)(T); + SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0) + : _obj(obj), _getter(getter), _setter(setter) {} + virtual ~SGRawValueMethods () {} + virtual T getValue () const { + if (_getter) { return (_obj.*_getter)(); } + else { return SGRawValue::DefaultValue; } + } + virtual bool setValue (T value) { + if (_setter) { (_obj.*_setter)(value); return true; } + else return false; + } + virtual SGRawValue * clone () const { + return new SGRawValueMethods(_obj, _getter, _setter); + } +private: + C &_obj; + getter_t _getter; + setter_t _setter; +}; + + +/** + * An indexed value managed through an object and access methods. + * + * A read-only value will not have a setter; a write-only value will + * not have a getter. + */ +template +class SGRawValueMethodsIndexed : public SGRawValue +{ +public: + typedef T (C::*getter_t)(int) const; + typedef void (C::*setter_t)(int, T); + SGRawValueMethodsIndexed (C &obj, int index, + getter_t getter = 0, setter_t setter = 0) + : _obj(obj), _index(index), _getter(getter), _setter(setter) {} + virtual ~SGRawValueMethodsIndexed () {} + virtual T getValue () const { + if (_getter) { return (_obj.*_getter)(_index); } + else { return SGRawValue::DefaultValue; } + } + virtual bool setValue (T value) { + if (_setter) { (_obj.*_setter)(_index, value); return true; } + else return false; + } + virtual SGRawValue * clone () const { + return new SGRawValueMethodsIndexed(_obj, _index, _getter, _setter); + } +private: + C &_obj; + int _index; + getter_t _getter; + setter_t _setter; +}; + + +/** + * The smart pointer that manage reference counting + */ +class SGPropertyNode; +class SGPropertyNode_ptr +{ +public: + + /** + * Default constructor + */ + SGPropertyNode_ptr(); + + /** + * Copy constructor + */ + SGPropertyNode_ptr( const SGPropertyNode_ptr &r ); + + /** + * Constructor from a pointer to a node + */ + SGPropertyNode_ptr( SGPropertyNode *p ); + + /** + * Destructor + */ + ~SGPropertyNode_ptr(); + + /** + * Assignement operator + */ + SGPropertyNode_ptr &operator=( const SGPropertyNode_ptr &r ); + + /** + * Pointer access operator + */ + SGPropertyNode *operator->(); + + /** + * Pointer access operator (const) + */ + const SGPropertyNode *operator->() const; + + /** + * Conversion to SGPropertyNode * operator + */ + operator SGPropertyNode *(); + + /** + * Conversion to const SGPropertyNode * operator + */ + operator const SGPropertyNode *() const; + + /** + * Return the pointer. + */ + SGPropertyNode * ptr () { return _ptr; } + + /** + * Validity test + */ + bool valid() const; + +private: + + SGPropertyNode *_ptr; +}; + + + +/** + * The property change listener interface. + * + *

Any class that needs to listen for property changes must implement + * this interface.

+ */ +class SGPropertyChangeListener +{ +public: + virtual ~SGPropertyChangeListener (); + virtual void valueChanged (SGPropertyNode * node); + virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child); + virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child); + +protected: + friend class SGPropertyNode; + virtual void register_property (SGPropertyNode * node); + virtual void unregister_property (SGPropertyNode * node); + +private: + vector _properties; +}; + + + +/** + * A node in a property tree. + */ +class SGPropertyNode +{ +public: + + /** + * Public constants. + */ + enum { + MAX_STRING_LEN = 1024 + }; + + /** + * Property value types. + */ + enum Type { + NONE, + ALIAS, + BOOL, + INT, + LONG, + FLOAT, + DOUBLE, + STRING, + UNSPECIFIED + }; + + + /** + * Access mode attributes. + * + *

The ARCHIVE attribute is strictly advisory, and controls + * whether the property should normally be saved and restored.

+ */ + enum Attribute { + READ = 1, + WRITE = 2, + ARCHIVE = 4, + REMOVED = 8, + TRACE_READ = 16, + TRACE_WRITE = 32 + }; + + + /** + * Last used attribute + * Update as needed when enum Attribute is changed + */ + static const int LAST_USED_ATTRIBUTE; + + + /** + * Default constructor. + */ + SGPropertyNode (); + + + /** + * Copy constructor. + */ + SGPropertyNode (const SGPropertyNode &node); + + + /** + * Destructor. + */ + virtual ~SGPropertyNode (); + + + + // + // Basic properties. + // + + /** + * Test whether this node contains a primitive leaf value. + */ + bool hasValue () const { return (_type != NONE); } + + + /** + * Get the node's simple (XML) name. + */ + const char * getName () const { return _name; } + + + /** + * Get the node's pretty display name, with subscript when needed. + */ + const char * getDisplayName (bool simplify = false) const; + + + /** + * Get the node's integer index. + */ + int getIndex () const { return _index; } + + + /** + * Get a non-const pointer to the node's parent. + */ + SGPropertyNode * getParent () { return _parent; } + + + /** + * Get a const pointer to the node's parent. + */ + const SGPropertyNode * getParent () const { return _parent; } + + + // + // Children. + // + + + /** + * Get the number of child nodes. + */ + int nChildren () const { return _children.size(); } + + + /** + * Get a child node by position (*NOT* index). + */ + SGPropertyNode * getChild (int position); + + + /** + * Get a const child node by position (*NOT* index). + */ + const SGPropertyNode * getChild (int position) const; + + + /** + * Test whether a named child exists. + */ + bool hasChild (const char * name, int index = 0) const + { + return (getChild(name, index) != 0); + } + + + /** + * Get a child node by name and index. + */ + SGPropertyNode * getChild (const char * name, int index = 0, + bool create = false); + + + /** + * Get a const child node by name and index. + */ + const SGPropertyNode * getChild (const char * name, int index = 0) const; + + + /** + * Get a vector of all children with the specified name. + */ + vector getChildren (const char * name) const; + + + /** + * Remove a child node + */ + SGPropertyNode_ptr removeChild (const char * name, int index = 0, + bool keep = true); + + + // + // Alias support. + // + + + /** + * Alias this node's leaf value to another's. + */ + bool alias (SGPropertyNode * target); + + + /** + * Alias this node's leaf value to another's by relative path. + */ + bool alias (const char * path); + + + /** + * Remove any alias for this node. + */ + bool unalias (); + + + /** + * Test whether the node's leaf value is aliased to another's. + */ + bool isAlias () const { return (_type == ALIAS); } + + + /** + * Get a non-const pointer to the current alias target, if any. + */ + SGPropertyNode * getAliasTarget (); + + + /** + * Get a const pointer to the current alias target, if any. + */ + const SGPropertyNode * getAliasTarget () const; + + + // + // Path information. + // + + + /** + * Get the path to this node from the root. + */ + const char * getPath (bool simplify = false) const; + + + /** + * Get a pointer to the root node. + */ + SGPropertyNode * getRootNode (); + + + /** + * Get a const pointer to the root node. + */ + const SGPropertyNode * getRootNode () const; + + + /** + * Get a pointer to another node by relative path. + */ + SGPropertyNode * getNode (const char * relative_path, bool create = false); + + + /** + * Get a pointer to another node by relative path. + * + * This method leaves the index off the last member of the path, + * so that the user can specify it separately (and save some + * string building). For example, getNode("/bar[1]/foo", 3) is + * exactly equivalent to getNode("bar[1]/foo[3]"). The index + * provided overrides any given in the path itself for the last + * component. + */ + SGPropertyNode * getNode (const char * relative_path, int index, + bool create = false); + + + /** + * Get a const pointer to another node by relative path. + */ + const SGPropertyNode * getNode (const char * relative_path) const; + + + /** + * Get a const pointer to another node by relative path. + * + * This method leaves the index off the last member of the path, + * so that the user can specify it separate. + */ + const SGPropertyNode * getNode (const char * relative_path, + int index) const; + + + // + // Access Mode. + // + + /** + * Check a single mode attribute for the property node. + */ + bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); } + + + /** + * Set a single mode attribute for the property node. + */ + void setAttribute (Attribute attr, bool state) { + (state ? _attr |= attr : _attr &= ~attr); + } + + + /** + * Get all of the mode attributes for the property node. + */ + int getAttributes () const { return _attr; } + + + /** + * Set all of the mode attributes for the property node. + */ + void setAttributes (int attr) { _attr = attr; } + + + // + // Leaf Value (primitive). + // + + + /** + * Get the type of leaf value, if any, for this node. + */ + Type getType () const; + + + /** + * Get a bool value for this node. + */ + bool getBoolValue () const; + + + /** + * Get an int value for this node. + */ + int getIntValue () const; + + + /** + * Get a long int value for this node. + */ + long getLongValue () const; + + + /** + * Get a float value for this node. + */ + float getFloatValue () const; + + + /** + * Get a double value for this node. + */ + double getDoubleValue () const; + + + /** + * Get a string value for this node. + */ + const char * getStringValue () const; + + + + /** + * Set a bool value for this node. + */ + bool setBoolValue (bool value); + + + /** + * Set an int value for this node. + */ + bool setIntValue (int value); + + + /** + * Set a long int value for this node. + */ + bool setLongValue (long value); + + + /** + * Set a float value for this node. + */ + bool setFloatValue (float value); + + + /** + * Set a double value for this node. + */ + bool setDoubleValue (double value); + + + /** + * Set a string value for this node. + */ + bool setStringValue (const char * value); + + + /** + * Set a value of unspecified type for this node. + */ + bool setUnspecifiedValue (const char * value); + + + // + // Data binding. + // + + + /** + * Test whether this node is bound to an external data source. + */ + bool isTied () const { return _tied; } + + + /** + * Bind this node to an external bool source. + */ + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + + /** + * Bind this node to an external int source. + */ + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + + /** + * Bind this node to an external long int source. + */ + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + + /** + * Bind this node to an external float source. + */ + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + + /** + * Bind this node to an external double source. + */ + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + + /** + * Bind this node to an external string source. + */ + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + + /** + * Unbind this node from any external data source. + */ + bool untie (); + + + // + // Convenience methods using paths. + // TODO: add attribute methods + // + + + /** + * Get another node's type. + */ + Type getType (const char * relative_path) const; + + + /** + * Test whether another node has a leaf value. + */ + bool hasValue (const char * relative_path) const; + + + /** + * Get another node's value as a bool. + */ + bool getBoolValue (const char * relative_path, + bool defaultValue = false) const; + + + /** + * Get another node's value as an int. + */ + int getIntValue (const char * relative_path, + int defaultValue = 0) const; + + + /** + * Get another node's value as a long int. + */ + long getLongValue (const char * relative_path, + long defaultValue = 0L) const; + + + /** + * Get another node's value as a float. + */ + float getFloatValue (const char * relative_path, + float defaultValue = 0.0) const; + + + /** + * Get another node's value as a double. + */ + double getDoubleValue (const char * relative_path, + double defaultValue = 0.0L) const; + + + /** + * Get another node's value as a string. + */ + const char * getStringValue (const char * relative_path, + const char * defaultValue = "") const; + + + /** + * Set another node's value as a bool. + */ + bool setBoolValue (const char * relative_path, bool value); + + + /** + * Set another node's value as an int. + */ + bool setIntValue (const char * relative_path, int value); + + + /** + * Set another node's value as a long int. + */ + bool setLongValue (const char * relative_path, long value); + + + /** + * Set another node's value as a float. + */ + bool setFloatValue (const char * relative_path, float value); + + + /** + * Set another node's value as a double. + */ + bool setDoubleValue (const char * relative_path, double value); + + + /** + * Set another node's value as a string. + */ + bool setStringValue (const char * relative_path, const char * value); + + + /** + * Set another node's value with no specified type. + */ + bool setUnspecifiedValue (const char * relative_path, const char * value); + + + /** + * Test whether another node is bound to an external data source. + */ + bool isTied (const char * relative_path) const; + + + /** + * Bind another node to an external bool source. + */ + bool tie (const char * relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + + /** + * Bind another node to an external int source. + */ + bool tie (const char * relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + + /** + * Bind another node to an external long int source. + */ + bool tie (const char * relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + + /** + * Bind another node to an external float source. + */ + bool tie (const char * relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + + /** + * Bind another node to an external double source. + */ + bool tie (const char * relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + + /** + * Bind another node to an external string source. + */ + bool tie (const char * relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + + /** + * Unbind another node from any external data source. + */ + bool untie (const char * relative_path); + + + /** + * Add a change listener to the property. + */ + void addChangeListener (SGPropertyChangeListener * listener); + + + /** + * Remove a change listener from the property. + */ + void removeChangeListener (SGPropertyChangeListener * listener); + + + /** + * Fire a value change event to all listeners. + */ + void fireValueChanged (); + + + /** + * Fire a child-added event to all listeners. + */ + void fireChildAdded (SGPropertyNode * child); + + + /** + * Fire a child-removed event to all listeners. + */ + void fireChildRemoved (SGPropertyNode * child); + + +protected: + + void fireValueChanged (SGPropertyNode * node); + void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child); + void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child); + + /** + * Protected constructor for making new nodes on demand. + */ + SGPropertyNode (const char * name, int index, SGPropertyNode * parent); + + +private: + + // Get the raw value + bool get_bool () const; + int get_int () const; + long get_long () const; + float get_float () const; + double get_double () const; + const char * get_string () const; + + // Set the raw value + bool set_bool (bool value); + bool set_int (int value); + bool set_long (long value); + bool set_float (float value); + bool set_double (double value); + bool set_string (const char * value); + + + /** + * Clear any existing value and set the type to NONE. + */ + void clear_value (); + + + /** + * Get the value as a string. + */ + const char * make_string () const; + + + /** + * Trace a read access. + */ + void trace_read () const; + + + /** + * Trace a write access. + */ + void trace_write () const; + + + /** + * Increment reference counter + */ + void incrementRef(); + + /** + * Decrement reference counter + */ + int decrementRef(); + + friend class SGPropertyNode_ptr; + + + mutable char _buffer[MAX_STRING_LEN+1]; + + class hash_table; + + char * _name; + mutable char * _display_name; + int _index; + SGPropertyNode * _parent; + vector _children; + vector _removedChildren; + mutable char * _path; + hash_table * _path_cache; + Type _type; + bool _tied; + int _attr; + int _count; + + // The right kind of pointer... + union { + SGPropertyNode * alias; + SGRawValue * bool_val; + SGRawValue * int_val; + SGRawValue * long_val; + SGRawValue * float_val; + SGRawValue * double_val; + SGRawValue * string_val; + } _value; + + union { + bool bool_val; + int int_val; + long long_val; + float float_val; + double double_val; + char * string_val; + } _local_val; + + vector * _listeners; + + + + /** + * A very simple hash table with no remove functionality. + */ + class hash_table { + public: + + /** + * An entry in a bucket in a hash table. + */ + class entry { + public: + entry (); + virtual ~entry (); + virtual const char * get_key () { return _key; } + virtual void set_key (const char * key); + virtual SGPropertyNode * get_value () { return _value; } + virtual void set_value (SGPropertyNode * value); + private: + char * _key; + SGPropertyNode * _value; + }; + + + /** + * A bucket in a hash table. + */ + class bucket { + public: + bucket (); + virtual ~bucket (); + virtual entry * get_entry (const char * key, bool create = false); + private: + int _length; + entry ** _entries; + }; + + friend class bucket; + + hash_table (); + virtual ~hash_table (); + virtual SGPropertyNode * get (const char * key); + virtual void put (const char * key, SGPropertyNode * value); + + private: + unsigned int hashcode (const char * key); + unsigned int _data_length; + bucket ** _data; + }; + +}; + +#endif // __PROPS_HXX + +// end of props.hxx diff --git a/simgear/props/props_io.cxx b/simgear/props/props_io.cxx new file mode 100644 index 00000000..e808e0b6 --- /dev/null +++ b/simgear/props/props_io.cxx @@ -0,0 +1,586 @@ + +#include + +#include // atof() atoi() + +#include +#include +#include +#include + +#include "props.hxx" +#include "props_io.hxx" + +#include STL_IOSTREAM +#include STL_FSTREAM +#include STL_STRING +#include +#include + +SG_USING_STD(istream); +SG_USING_STD(ifstream); +SG_USING_STD(ostream); +SG_USING_STD(ofstream); +SG_USING_STD(string); +SG_USING_STD(vector); +SG_USING_STD(map); + +#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE) + + + +//////////////////////////////////////////////////////////////////////// +// Property list visitor, for XML parsing. +//////////////////////////////////////////////////////////////////////// + +class PropsVisitor : public XMLVisitor +{ +public: + + PropsVisitor (SGPropertyNode * root, const string &base) + : _root(root), _level(0), _base(base), _hasException(false) {} + + virtual ~PropsVisitor () {} + + void startXML (); + void endXML (); + void startElement (const char * name, const XMLAttributes &atts); + void endElement (const char * name); + void data (const char * s, int length); + void warning (const char * message, int line, int column); + + bool hasException () const { return _hasException; } + sg_io_exception &getException () { return _exception; } + void setException (const sg_io_exception &exception) { + _exception = exception; + _hasException = true; + } + +private: + + struct State + { + State () : node(0), type(""), mode(DEFAULT_MODE) {} + State (SGPropertyNode * _node, const char * _type, int _mode) + : node(_node), type(_type), mode(_mode) {} + SGPropertyNode * node; + string type; + int mode; + map counters; + }; + + State &state () { return _state_stack[_state_stack.size() - 1]; } + + void push_state (SGPropertyNode * node, const char * type, int mode) { + if (type == 0) + _state_stack.push_back(State(node, "unspecified", mode)); + else + _state_stack.push_back(State(node, type, mode)); + _level++; + _data = ""; + } + + void pop_state () { + _state_stack.pop_back(); + _level--; + } + + string _data; + SGPropertyNode * _root; + int _level; + vector _state_stack; + string _base; + sg_io_exception _exception; + bool _hasException; +}; + +void +PropsVisitor::startXML () +{ + _level = 0; + _state_stack.resize(0); +} + +void +PropsVisitor::endXML () +{ + _level = 0; + _state_stack.resize(0); +} + + +/** + * Check a yes/no flag, with default. + */ +static bool +checkFlag (const char * flag, bool defaultState = true) +{ + if (flag == 0) + return defaultState; + else if (string(flag) == "y") + return true; + else if (string(flag) == "n") + return false; + else { + string message = "Unrecognized flag value '"; + message += flag; + message += '\''; + // FIXME: add location info + throw sg_io_exception(message, "SimGear Property Reader"); + } +} + +void +PropsVisitor::startElement (const char * name, const XMLAttributes &atts) +{ + State &st = state(); + const char * attval; + + if (_level == 0) { + if (string(name) != (string)"PropertyList") { + string message = "Root element name is "; + message += name; + message += "; expected PropertyList"; + throw sg_io_exception(message, "SimGear Property Reader"); + } + + // Check for an include. + attval = atts.getValue("include"); + if (attval != 0) { + SGPath path(SGPath(_base).dir()); + path.append(attval); + try { + readProperties(path.str(), _root); + } catch (sg_io_exception &e) { + setException(e); + } + } + + push_state(_root, "", DEFAULT_MODE); + } + + else { + // Get the index. + attval = atts.getValue("n"); + int index = 0; + if (attval != 0) { + index = atoi(attval); + st.counters[name] = SG_MAX2(st.counters[name], index+1); + } else { + index = st.counters[name]; + st.counters[name]++; + } + + // Got the index, so grab the node. + SGPropertyNode * node = st.node->getChild(name, index, true); + + // Get the access-mode attributes, + // but don't set yet (in case they + // prevent us from recording the value). + int mode = 0; + + attval = atts.getValue("read"); + if (checkFlag(attval, true)) + mode |= SGPropertyNode::READ; + attval = atts.getValue("write"); + if (checkFlag(attval, true)) + mode |= SGPropertyNode::WRITE; + attval = atts.getValue("archive"); + if (checkFlag(attval, false)) + mode |= SGPropertyNode::ARCHIVE; + attval = atts.getValue("trace-read"); + if (checkFlag(attval, false)) + mode |= SGPropertyNode::TRACE_READ; + attval = atts.getValue("trace-write"); + if (checkFlag(attval, false)) + mode |= SGPropertyNode::TRACE_WRITE; + + // Check for an alias. + attval = atts.getValue("alias"); + if (attval != 0) { + if (!node->alias(attval)) + SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval); + } + + // Check for an include. + attval = atts.getValue("include"); + if (attval != 0) { + SGPath path(SGPath(_base).dir()); + path.append(attval); + try { + readProperties(path.str(), node); + } catch (sg_io_exception &e) { + setException(e); + } + } + + push_state(node, atts.getValue("type"), mode); + } +} + +void +PropsVisitor::endElement (const char * name) +{ + State &st = state(); + bool ret; + + // If there are no children and it's + // not an alias, then it's a leaf value. + if (st.node->nChildren() == 0 && !st.node->isAlias()) { + if (st.type == "bool") { + if (_data == "true" || atoi(_data.c_str()) != 0) + ret = st.node->setBoolValue(true); + else + ret = st.node->setBoolValue(false); + } else if (st.type == "int") { + ret = st.node->setIntValue(atoi(_data.c_str())); + } else if (st.type == "long") { + ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0)); + } else if (st.type == "float") { + ret = st.node->setFloatValue(atof(_data.c_str())); + } else if (st.type == "double") { + ret = st.node->setDoubleValue(strtod(_data.c_str(), 0)); + } else if (st.type == "string") { + ret = st.node->setStringValue(_data.c_str()); + } else if (st.type == "unspecified") { + ret = st.node->setUnspecifiedValue(_data.c_str()); + } else { + string message = "Unrecognized data type '"; + message += st.type; + message += '\''; + // FIXME: add location information + throw sg_io_exception(message, "SimGear Property Reader"); + } + if (!ret) + SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set " + << st.node->getPath() << " to value \"" + << _data << "\" with type " << st.type); + } + + // Set the access-mode attributes now, + // once the value has already been + // assigned. + st.node->setAttributes(st.mode); + + pop_state(); +} + +void +PropsVisitor::data (const char * s, int length) +{ + if (state().node->nChildren() == 0) + _data.append(string(s, length)); +} + +void +PropsVisitor::warning (const char * message, int line, int column) +{ + SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: " + << message << " at line " << line << ", column " << column); +} + + + +//////////////////////////////////////////////////////////////////////// +// Property list reader. +//////////////////////////////////////////////////////////////////////// + + +/** + * Read properties from an input stream. + * + * @param input The input stream containing an XML property file. + * @param start_node The root node for reading properties. + * @param base A base path for resolving external include references. + * @return true if the read succeeded, false otherwise. + */ +void +readProperties (istream &input, SGPropertyNode * start_node, + const string &base) +{ + PropsVisitor visitor(start_node, base); + readXML(input, visitor, base); + if (visitor.hasException()) + throw visitor.getException(); +} + + +/** + * Read properties from a file. + * + * @param file A string containing the file path. + * @param start_node The root node for reading properties. + * @return true if the read succeeded, false otherwise. + */ +void +readProperties (const string &file, SGPropertyNode * start_node) +{ + PropsVisitor visitor(start_node, file); + readXML(file, visitor); + if (visitor.hasException()) + throw visitor.getException(); +} + + + +//////////////////////////////////////////////////////////////////////// +// Property list writer. +//////////////////////////////////////////////////////////////////////// + +#define INDENT_STEP 2 + +/** + * Return the type name. + */ +static const char * +getTypeName (SGPropertyNode::Type type) +{ + switch (type) { + case SGPropertyNode::UNSPECIFIED: + return "unspecified"; + case SGPropertyNode::BOOL: + return "bool"; + case SGPropertyNode::INT: + return "int"; + case SGPropertyNode::LONG: + return "long"; + case SGPropertyNode::FLOAT: + return "float"; + case SGPropertyNode::DOUBLE: + return "double"; + case SGPropertyNode::STRING: + return "string"; + case SGPropertyNode::ALIAS: + case SGPropertyNode::NONE: + return "unspecified"; + } + + // keep the compiler from squawking + return "unspecified"; +} + + +/** + * Escape characters for output. + */ +static void +writeData (ostream &output, const string &data) +{ + for (int i = 0; i < (int)data.size(); i++) { + switch (data[i]) { + case '&': + output << "&"; + break; + case '<': + output << "<"; + break; + case '>': + output << ">"; + break; + default: + output << data[i]; + break; + } + } +} + +static void +doIndent (ostream &output, int indent) +{ + while (indent-- > 0) { + output << ' '; + } +} + + +static void +writeAtts (ostream &output, const SGPropertyNode * node) +{ + int index = node->getIndex(); + + if (index != 0) + output << " n=\"" << index << '"'; + +#if 0 + if (!node->getAttribute(SGPropertyNode::READ)) + output << " read=\"n\""; + + if (!node->getAttribute(SGPropertyNode::WRITE)) + output << " write=\"n\""; + + if (node->getAttribute(SGPropertyNode::ARCHIVE)) + output << " archive=\"y\""; +#endif + +} + + +/** + * Test whether a node is archivable or has archivable descendants. + */ +static bool +isArchivable (const SGPropertyNode * node) +{ + // FIXME: it's inefficient to do this all the time + if (node->getAttribute(SGPropertyNode::ARCHIVE)) + return true; + else { + int nChildren = node->nChildren(); + for (int i = 0; i < nChildren; i++) + if (isArchivable(node->getChild(i))) + return true; + } + return false; +} + + +static bool +writeNode (ostream &output, const SGPropertyNode * node, + bool write_all, int indent) +{ + // Don't write the node or any of + // its descendants unless it is + // allowed to be archived. + if (!write_all && !isArchivable(node)) + return true; // Everything's OK, but we won't write. + + const string name = node->getName(); + int nChildren = node->nChildren(); + + // If there is a literal value, + // write it first. + if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) { + doIndent(output, indent); + output << '<' << name; + writeAtts(output, node); + if (node->isAlias() && node->getAliasTarget() != 0) { + output << " alias=\"" << node->getAliasTarget()->getPath() + << "\"/>" << endl; + } else { + if (node->getType() != SGPropertyNode::UNSPECIFIED) + output << " type=\"" << getTypeName(node->getType()) << '"'; + output << '>'; + writeData(output, node->getStringValue()); + output << "' << endl; + } + } + + // If there are children, write them next. + if (nChildren > 0 || node->isAlias()) { + doIndent(output, indent); + output << '<' << name; + writeAtts(output, node); + output << '>' << endl; + for (int i = 0; i < nChildren; i++) + writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP); + doIndent(output, indent); + output << "' << endl; + } + + return true; +} + + +void +writeProperties (ostream &output, const SGPropertyNode * start_node, + bool write_all) +{ + int nChildren = start_node->nChildren(); + + output << "" << endl << endl; + output << "" << endl; + + for (int i = 0; i < nChildren; i++) { + writeNode(output, start_node->getChild(i), write_all, INDENT_STEP); + } + + output << "" << endl; +} + + +void +writeProperties (const string &file, const SGPropertyNode * start_node, + bool write_all) +{ + ofstream output(file.c_str()); + if (output.good()) { + writeProperties(output, start_node, write_all); + } else { + throw sg_io_exception("Cannot open file", sg_location(file)); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Copy properties from one tree to another. +//////////////////////////////////////////////////////////////////////// + + +/** + * Copy one property tree to another. + * + * @param in The source property tree. + * @param out The destination property tree. + * @return true if all properties were copied, false if some failed + * (for example, if the property's value is tied read-only). + */ +bool +copyProperties (const SGPropertyNode *in, SGPropertyNode *out) +{ + bool retval = true; + + // First, copy the actual value, + // if any. + if (in->hasValue()) { + switch (in->getType()) { + case SGPropertyNode::BOOL: + if (!out->setBoolValue(in->getBoolValue())) + retval = false; + break; + case SGPropertyNode::INT: + if (!out->setIntValue(in->getIntValue())) + retval = false; + break; + case SGPropertyNode::LONG: + if (!out->setLongValue(in->getLongValue())) + retval = false; + break; + case SGPropertyNode::FLOAT: + if (!out->setFloatValue(in->getFloatValue())) + retval = false; + break; + case SGPropertyNode::DOUBLE: + if (!out->setDoubleValue(in->getDoubleValue())) + retval = false; + break; + case SGPropertyNode::STRING: + if (!out->setStringValue(in->getStringValue())) + retval = false; + break; + case SGPropertyNode::UNSPECIFIED: + if (!out->setUnspecifiedValue(in->getStringValue())) + retval = false; + break; + default: + string message = "Unknown internal SGPropertyNode type"; + message += in->getType(); + throw sg_error(message, "SimGear Property Reader"); + } + } + + // Next, copy the children. + int nChildren = in->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode * in_child = in->getChild(i); + SGPropertyNode * out_child = out->getChild(in_child->getName(), + in_child->getIndex(), + true); + if (!copyProperties(in_child, out_child)) + retval = false; + } + + return retval; +} + +// end of props_io.cxx diff --git a/simgear/props/props_io.hxx b/simgear/props/props_io.hxx new file mode 100644 index 00000000..d541e1ce --- /dev/null +++ b/simgear/props/props_io.hxx @@ -0,0 +1,66 @@ +/** + * \file props_io.hxx + * Interface definition for property list io. + * Started Fall 2000 by David Megginson, david@megginson.com + * This code is released into the Public Domain. + * + * See props.html for documentation [replace with URL when available]. + * + * $Id$ + */ + +#ifndef __PROPS_IO_HXX +#define __PROPS_IO_HXX + +#include +#include + +#include + +#include STL_STRING +#include +#include +#include STL_IOSTREAM + +SG_USING_STD(string); +SG_USING_STD(vector); +SG_USING_STD(map); +SG_USING_STD(istream); +SG_USING_STD(ostream); + +/** + * Read properties from an XML input stream. + */ +void readProperties (istream &input, SGPropertyNode * start_node, + const string &base = ""); + + +/** + * Read properties from an XML file. + */ +void readProperties (const string &file, SGPropertyNode * start_node); + + +/** + * Write properties to an XML output stream. + */ +void writeProperties (ostream &output, const SGPropertyNode * start_node, + bool write_all = false); + + +/** + * Write properties to an XML file. + */ +void writeProperties (const string &file, const SGPropertyNode * start_node, + bool write_all = false); + + +/** + * Copy properties from one node to another. + */ +bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out); + + +#endif // __PROPS_IO_HXX + +// end of props_io.hxx diff --git a/simgear/props/props_test.cxx b/simgear/props/props_test.cxx new file mode 100644 index 00000000..ab29deee --- /dev/null +++ b/simgear/props/props_test.cxx @@ -0,0 +1,346 @@ + +//////////////////////////////////////////////////////////////////////// +// Test harness. +//////////////////////////////////////////////////////////////////////// + +#include + +#include STL_IOSTREAM +#include "props.hxx" +#include "props_io.hxx" + +SG_USING_STD(cout); +SG_USING_STD(cerr); +SG_USING_STD(endl); + + + +//////////////////////////////////////////////////////////////////////// +// Sample object. +//////////////////////////////////////////////////////////////////////// + +class Stuff { +public: + Stuff () : _stuff(199.0) {} + virtual float getStuff () const { return _stuff; } + virtual void setStuff (float stuff) { _stuff = stuff; } + virtual float getStuff (int index) const { return _stuff * index; } + virtual void setStuff (int index, float val) { + _stuff = val / index; + } +private: + float _stuff; +}; + + + +//////////////////////////////////////////////////////////////////////// +// Sample function. +//////////////////////////////////////////////////////////////////////// + +static int get100 () { return 100; } + +static double getNum (int index) { return 1.0 / index; } + + + +//////////////////////////////////////////////////////////////////////// +// Show a value. +//////////////////////////////////////////////////////////////////////// + +static void +show_values (const SGPropertyNode * node) +{ + cout << "Bool: " << (node->getBoolValue() ? "true" : "false") << endl; + cout << "Int: " << node->getIntValue() << endl; + cout << "Float: " << node->getFloatValue() << endl; + cout << "Double: " << node->getDoubleValue() << endl; + cout << "String: " << node->getStringValue() << endl; +} + + + +//////////////////////////////////////////////////////////////////////// +// Test individual values. +//////////////////////////////////////////////////////////////////////// + +static void +test_value () +{ + SGPropertyNode * node; + + cout << endl << "Value" << endl << endl; + + // + // Test coercion for getters. + // + + cout << "Testing coercion from bool (expect true)" << endl; + node = new SGPropertyNode; + node->setBoolValue(true); + show_values(node); + delete node; + cout << endl; + + cout << "Testing coercion from int (expect 128)" << endl; + node = new SGPropertyNode; + node->setIntValue(128); + show_values(node); + delete node; + cout << endl; + + cout << "Testing coercion from float (expect 1.0/3.0)" << endl; + node = new SGPropertyNode; + node->setFloatValue(1.0/3.0); + show_values(node); + delete node; + cout << endl; + + cout << "Testing coercion from double (expect 1.0/3.0)" << endl; + node = new SGPropertyNode; + node->setDoubleValue(1.0/3.0); + show_values(node); + delete node; + cout << endl; + + cout << "Testing coercion from string (expect 10e4)" << endl; + node = new SGPropertyNode; + node->setStringValue("10e4"); + show_values(node); + delete node; + cout << endl; + + cout << "Testing coercion from unspecified (expect -10e-4)" << endl; + node = new SGPropertyNode; + node->setUnspecifiedValue("-10e-4"); + show_values(node); + delete node; + cout << endl; + + // + // Test coercion for setters. + // + + node = new SGPropertyNode; + + cout << "Testing coercion to bool from bool (expect false)" << endl; + node->setBoolValue(false); + show_values(node); + cout << endl; + + cout << "Testing coercion to bool from int (expect 1)" << endl; + node->setIntValue(1); + show_values(node); + cout << endl; + + cout << "Testing coercion to bool from float (expect 1.1)" << endl; + node->setFloatValue(1.1); + show_values(node); + cout << endl; + + cout << "Testing coercion to bool from double (expect 1.1)" << endl; + node->setDoubleValue(1.1); + show_values(node); + cout << endl; + + cout << "Testing coercion to bool from string (expect 1e10)" << endl; + node->setStringValue("1e10"); + show_values(node); + cout << endl; + + cout << "Testing coercion to bool from unspecified (expect 1e10)" << endl; + node->setUnspecifiedValue("1e10"); + show_values(node); + cout << endl; + + // Test tying to a pointer. + + static int myValue = 10; + + cout << "Testing tying to a pointer (expect 10)" << endl; + if (!node->tie(SGRawValuePointer(&myValue), false)) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Changing base variable (expect -5)" << endl; + myValue = -5; + show_values(node); + if (!node->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + cout << endl; + + + // Test tying to static functions. + + cout << "Create a new int value (expect 10)" << endl; + node->setIntValue(10); + show_values(node); + cout << endl; + + cout << "Testing tying to static getter (expect 100)" << endl; + if (!node->tie(SGRawValueFunctions(get100))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Try changing value with no setter (expect 100)" << endl; + if (node->setIntValue(200)) + cout << "*** setIntValue did not return false!!!" << endl; + show_values(node); + cout << endl; + + cout << "Untie value (expect 100)" << endl; + if (!node->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Try changing value (expect 200)" << endl; + if (!node->setIntValue(200)) + cout << "*** setIntValue RETURNED FALSE!!!" << endl; + show_values(node); + cout << endl; + + // Test tying to indexed static functions. + + cout << "Create a new int value (expect 10)" << endl; + node->setIntValue(10); + show_values(node); + cout << endl; + + cout << "Testing tying to indexed static getter (0.3333...)" << endl; + if (!node->tie(SGRawValueFunctionsIndexed(3, getNum))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Untie value (expect 0.3333...)" << endl; + if (!node->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + + // Test methods. + + cout << "Try tying to an object without defaults (expect 199)" << endl; + Stuff stuff; + SGRawValueMethods tiedstuff(stuff, + &Stuff::getStuff, + &Stuff::setStuff); + if (!node->tie(tiedstuff, false)) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Try untying from object (expect 199)" << endl; + if (!node->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Try tying to an indexed method (expect 199)" << endl; + if (!node->tie(SGRawValueMethodsIndexed + (stuff, 2, &Stuff::getStuff, &Stuff::setStuff))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + node->untie(); + + cout << "Change value (expect 50)" << endl; + if (!node->setIntValue(50)) + cout << "*** FAILED TO SET VALUE!!!" << endl; + show_values(node); + cout << endl; + + cout << "Try tying to an object with defaults (expect 50)" << endl; + if (!node->tie(tiedstuff, true)) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(node); + cout << endl; + + delete node; +} + + + +//////////////////////////////////////////////////////////////////////// +// Check property nodes. +//////////////////////////////////////////////////////////////////////// + +static void +dump_node (const SGPropertyNode * node) +{ + writeProperties(cout, node, true); +} + +static void +test_property_nodes () +{ + SGPropertyNode root; + cout << "Created root node " << root.getPath() << endl; + + SGPropertyNode * child = root.getChild("foo", 0, true); + + SGPropertyNode *grandchild = child->getChild("bar", 0, true); + grandchild->setDoubleValue(100); + grandchild = child->getChild("bar", 1, true); + grandchild->setDoubleValue(200); + grandchild = child->getChild("bar", 2, true); + grandchild->setDoubleValue(300); + grandchild = child->getChild("bar", 3, true); + grandchild->setDoubleValue(400); + + child = root.getChild("hack", 0, true); + + grandchild = child->getChild("bar", 0, true); + grandchild->setDoubleValue(100); + grandchild = child->getChild("bar", 3, true); + grandchild->setDoubleValue(200); + grandchild = child->getChild("bar", 1, true); + grandchild->setDoubleValue(300); + grandchild = child->getChild("bar", 2, true); + grandchild->setDoubleValue(400); + dump_node(&root); + + cout << "Trying path (expect /foo[0]/bar[0])" << endl; + grandchild = root.getNode("/hack/../foo/./bar[0]"); + cout << "Path is " << grandchild->getPath() << endl; + cout << endl; + + cout << "Looking for all /hack[0]/bar children" << endl; + vector bar = child->getChildren("bar"); + cout << "There are " << bar.size() << " matches" << endl; + for (int i = 0; i < (int)bar.size(); i++) + cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl; + cout << endl; + + cout << "Testing addition of a totally empty node" << endl; + if (root.getNode("/a/b/c", true) == 0) + cerr << "** failed to create /a/b/c" << endl; + dump_node(&root); + cout << endl; +} + + +int main (int ac, char ** av) +{ + test_value(); + test_property_nodes(); + + for (int i = 1; i < ac; i++) { + try { + cout << "Reading " << av[i] << endl; + SGPropertyNode root; + readProperties(av[i], &root); + writeProperties(cout, &root, true); + cout << endl; + } catch (string &message) { + cout << "Aborted with " << message << endl; + } + } + + return 0; +} diff --git a/simgear/scene/Makefile.am b/simgear/scene/Makefile.am new file mode 100644 index 00000000..494d96c6 --- /dev/null +++ b/simgear/scene/Makefile.am @@ -0,0 +1,13 @@ +includedir = @includedir@/scene + +SUBDIRS = model + +# lib_LIBRARIES = libsgscene.a + +# noinst_HEADERS = + +# include_HEADERS = + +# libsgscene_a_SOURCES = + +INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/model/Makefile.am b/simgear/scene/model/Makefile.am new file mode 100644 index 00000000..26de102c --- /dev/null +++ b/simgear/scene/model/Makefile.am @@ -0,0 +1,11 @@ +includedir = @includedir@/scene/model + +lib_LIBRARIES = libsgmodel.a + +noinst_HEADERS = + +include_HEADERS = location.hxx + +libsgmodel_a_SOURCES = location.cxx + +INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/model/location.cxx b/simgear/scene/model/location.cxx new file mode 100644 index 00000000..033cf613 --- /dev/null +++ b/simgear/scene/model/location.cxx @@ -0,0 +1,283 @@ +// location.cxx -- class for determining model location in the flightgear world. +// +// Written by Jim Wilson, David Megginson, started April 2002. +// Based largely on code by Curtis Olson and Norman Vine. +// +// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#include + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +// #include +// #include "globals.hxx" + +#include "location.hxx" + + +/** + * make model transformation Matrix - based on optimizations by NHV + */ +static void MakeTRANS( sgMat4 dst, const double Theta, + const double Phi, const double Psi, + const sgMat4 UP) +{ + SGfloat cosTheta = (SGfloat) cos(Theta); + SGfloat sinTheta = (SGfloat) sin(Theta); + SGfloat cosPhi = (SGfloat) cos(Phi); + SGfloat sinPhi = (SGfloat) sin(Phi); + SGfloat sinPsi = (SGfloat) sin(Psi) ; + SGfloat cosPsi = (SGfloat) cos(Psi) ; + + sgMat4 tmp; + + tmp[0][0] = cosPhi * cosTheta; + tmp[0][1] = sinPhi * cosPsi + cosPhi * -sinTheta * -sinPsi; + tmp[0][2] = sinPhi * sinPsi + cosPhi * -sinTheta * cosPsi; + + tmp[1][0] = -sinPhi * cosTheta; + tmp[1][1] = cosPhi * cosPsi + -sinPhi * -sinTheta * -sinPsi; + tmp[1][2] = cosPhi * sinPsi + -sinPhi * -sinTheta * cosPsi; + + tmp[2][0] = sinTheta; + tmp[2][1] = cosTheta * -sinPsi; + tmp[2][2] = cosTheta * cosPsi; + + float a = UP[0][0]; + float b = UP[1][0]; + float c = UP[2][0]; + dst[2][0] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ; + dst[1][0] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ; + dst[0][0] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ; + dst[3][0] = SG_ZERO ; + + a = UP[0][1]; + b = UP[1][1]; + c = UP[2][1]; + dst[2][1] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ; + dst[1][1] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ; + dst[0][1] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ; + dst[3][1] = SG_ZERO ; + + a = UP[0][2]; + c = UP[2][2]; + dst[2][2] = a*tmp[0][0] + c*tmp[0][2] ; + dst[1][2] = a*tmp[1][0] + c*tmp[1][2] ; + dst[0][2] = -(a*tmp[2][0] + c*tmp[2][2]) ; + dst[3][2] = SG_ZERO ; + + dst[2][3] = SG_ZERO ; + dst[1][3] = SG_ZERO ; + dst[0][3] = SG_ZERO ; + dst[3][3] = SG_ONE ; + +} + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGLocation. +//////////////////////////////////////////////////////////////////////// + +// Constructor +FGLocation::FGLocation( void ): + _dirty(true), + _lon_deg(0), + _lat_deg(0), + _alt_ft(0), + _roll_deg(0), + _pitch_deg(0), + _heading_deg(0), + _cur_elev_m(0), + _tile_center(0) +{ + sgdZeroVec3(_absolute_view_pos); + sgZeroVec3(_relative_view_pos); + sgZeroVec3(_zero_elev_view_pos); + sgMakeRotMat4( UP, 0.0, 0.0, 0.0 ); + sgMakeRotMat4( TRANS, 0.0, 0.0, 0.0 ); +} + + +// Destructor +FGLocation::~FGLocation( void ) { +} + +void +FGLocation::init () +{ +} + +void +FGLocation::bind () +{ +} + +void +FGLocation::unbind () +{ +} + +void +FGLocation::setPosition (double lon_deg, double lat_deg, double alt_ft) +{ + _dirty = true; + _lon_deg = lon_deg; + _lat_deg = lat_deg; + _alt_ft = alt_ft; +} + +void +FGLocation::setOrientation (double roll_deg, double pitch_deg, double heading_deg) +{ + _dirty = true; + _roll_deg = roll_deg; + _pitch_deg = pitch_deg; + _heading_deg = heading_deg; +} + +double * +FGLocation::get_absolute_view_pos( const Point3D scenery_center ) +{ + if ( _dirty ) { + recalc( scenery_center ); + } + return _absolute_view_pos; +} + +float * +FGLocation::getRelativeViewPos( const Point3D scenery_center ) +{ + if ( _dirty ) { + recalc( scenery_center ); + } + return _relative_view_pos; +} + +float * +FGLocation::getZeroElevViewPos( const Point3D scenery_center ) +{ + if ( _dirty ) { + recalc( scenery_center ); + } + return _zero_elev_view_pos; +} + + +// recalc() is done every time one of the setters is called (making the +// cached data "dirty") on the next "get". It calculates all the outputs +// for viewer. +void +FGLocation::recalc( const Point3D scenery_center ) +{ + + recalcPosition( _lon_deg, _lat_deg, _alt_ft, scenery_center ); + + // Make the world up rotation matrix for eye positioin... + sgMakeRotMat4( UP, _lon_deg, 0.0, -_lat_deg ); + + + // get the world up radial vector from planet center for output + sgSetVec3( _world_up, UP[0][0], UP[0][1], UP[0][2] ); + + // Creat local matrix with current geodetic position. Converting + // the orientation (pitch/roll/heading) to vectors. + MakeTRANS( TRANS, _pitch_deg * SG_DEGREES_TO_RADIANS, + _roll_deg * SG_DEGREES_TO_RADIANS, + -_heading_deg * SG_DEGREES_TO_RADIANS, + UP); + + // Given a vector pointing straight down (-Z), map into onto the + // local plane representing "horizontal". This should give us the + // local direction for moving "south". + sgVec3 minus_z; + sgSetVec3( minus_z, 0.0, 0.0, -1.0 ); + + sgmap_vec_onto_cur_surface_plane(_world_up, _relative_view_pos, minus_z, + _surface_south); + sgNormalizeVec3(_surface_south); + + // now calculate the surface east vector + sgVec3 world_down; + sgNegateVec3(world_down, _world_up); + sgVectorProductVec3(_surface_east, _surface_south, world_down); + + set_clean(); +} + +void +FGLocation::recalcPosition( double lon_deg, double lat_deg, double alt_ft, + const Point3D scenery_center ) const +{ + double sea_level_radius_m; + double lat_geoc_rad; + + + // Convert from geodetic to geocentric + // coordinates. + sgGeodToGeoc(lat_deg * SGD_DEGREES_TO_RADIANS, + alt_ft * SG_FEET_TO_METER, + &sea_level_radius_m, + &lat_geoc_rad); + + // Calculate the cartesian coordinates + // of point directly below at sea level. + // aka Zero Elevation Position + Point3D p = Point3D(lon_deg * SG_DEGREES_TO_RADIANS, + lat_geoc_rad, + sea_level_radius_m); + Point3D tmp = sgPolarToCart3d(p) - _tile_center; + sgSetVec3(_zero_elev_view_pos, tmp[0], tmp[1], tmp[2]); + + // Calculate the absolute view position + // in fgfs coordinates. + // aka Absolute View Position + p.setz(p.radius() + alt_ft * SG_FEET_TO_METER); + tmp = sgPolarToCart3d(p); + sgdSetVec3(_absolute_view_pos, tmp[0], tmp[1], tmp[2]); + + // Calculate the relative view position + // from the scenery center. + // aka Relative View Position + + // FIXME: view position should ONLY be calculated in the viewer... + // Anything else should calculate their own positions relative to the + // viewer's tile_center. + sgdVec3 center; + sgdSetVec3( center, + scenery_center.x(), scenery_center.y(), scenery_center.z() ); + sgdVec3 view_pos; + sgdSubVec3(view_pos, _absolute_view_pos, center); + sgSetVec3(_relative_view_pos, view_pos); + +} + +void +FGLocation::update (int dt) +{ +} diff --git a/simgear/scene/model/location.hxx b/simgear/scene/model/location.hxx new file mode 100644 index 00000000..0cba6378 --- /dev/null +++ b/simgear/scene/model/location.hxx @@ -0,0 +1,187 @@ +// location.hxx -- class for determining model location in the flightgear world. +// +// Written by Jim Wilson, David Megginson, started April 2002. +// +// Copyright (C) 2002 Jim Wilson, David Megginson +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#ifndef _LOCATION_HXX +#define _LOCATION_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include +#include +#include + +#include // plib include + +// #include "fgfs.hxx" + + +// Define a structure containing view information +class FGLocation +{ + +public: + + // Constructor + FGLocation( void ); + + // Destructor + virtual ~FGLocation( void ); + + ////////////////////////////////////////////////////////////////////// + // Part 1: standard FGSubsystem implementation. + ////////////////////////////////////////////////////////////////////// + + virtual void init (); + virtual void bind (); + virtual void unbind (); + void update (int dt); + + + ////////////////////////////////////////////////////////////////////// + // Part 2: user settings. + ////////////////////////////////////////////////////////////////////// + + // Geodetic position of model... + virtual double getLongitude_deg () const { return _lon_deg; } + virtual double getLatitude_deg () const { return _lat_deg; } + virtual double getAltitudeASL_ft () const { return _alt_ft; } + virtual void setPosition (double lon_deg, double lat_deg, double alt_ft); + + + // Reference orientation rotations... + // These are rotations that represent the plane attitude effect on + // the view (in Pilot view). IE The view frustrum rotates as the plane + // turns, pitches, and rolls. + // In model view (lookat/chaseview) these end up changing the angle that + // the eye is looking at the ojbect (ie the model). + // FIXME: the FGModel class should have its own version of these so that + // it can generate it's own model rotations. + virtual double getRoll_deg () const { return _roll_deg; } + virtual double getPitch_deg () const {return _pitch_deg; } + virtual double getHeading_deg () const {return _heading_deg; } + virtual void setOrientation (double roll_deg, double pitch_deg, double heading_deg); + + + ////////////////////////////////////////////////////////////////////// + // Part 3: output vectors and matrices in FlightGear coordinates. + ////////////////////////////////////////////////////////////////////// + + // Vectors and positions... + + // Get zero view_pos + virtual float * get_view_pos() { return _relative_view_pos; } + // Get the absolute view position in fgfs coordinates. + virtual double * get_absolute_view_pos( const Point3D scenery_center ); + // Get zero elev + virtual float * get_zero_elev() { return _zero_elev_view_pos; } + // Get world up vector + virtual float *get_world_up() { return _world_up; } + // Get the relative (to scenery center) view position in fgfs coordinates. + virtual float * getRelativeViewPos( const Point3D scenery_center ); + // Get the absolute zero-elevation view position in fgfs coordinates. + virtual float * getZeroElevViewPos( const Point3D scenery_center ); + // Get surface east vector + virtual float *get_surface_east() { return _surface_east; } + // Get surface south vector + virtual float *get_surface_south() { return _surface_south; } + // Elevation of ground under location (based on scenery output)... + void set_cur_elev_m ( double elev ) { _cur_elev_m = elev; } + inline double get_cur_elev_m () { return _cur_elev_m; } + // Interface to current buckets for use with tilemgr... + void set_tile_center ( Point3D tile_center ) { _tile_center = tile_center; } + inline Point3D get_tile_center () { return _tile_center; } + + // Matrices... + virtual const sgVec4 *getTransformMatrix( const Point3D scenery_center ) { + if ( _dirty ) { + recalc( scenery_center ); + } + return TRANS; + } + virtual const sgVec4 *getCachedTransformMatrix() { return TRANS; } + virtual const sgVec4 *getUpMatrix( const Point3D scenery_center ) { + if ( _dirty ) { + recalc( scenery_center ); + } + return UP; + } + virtual const sgVec4 *getCachedUpMatrix() { return UP; } + + +private: + + ////////////////////////////////////////////////////////////////// + // private data // + ////////////////////////////////////////////////////////////////// + + // flag forcing a recalc of derived view parameters + bool _dirty; + + mutable sgdVec3 _absolute_view_pos; + mutable sgVec3 _relative_view_pos; + mutable sgVec3 _zero_elev_view_pos; + + double _lon_deg; + double _lat_deg; + double _alt_ft; + + double _roll_deg; + double _pitch_deg; + double _heading_deg; + + // elevation of ground under this location... + double _cur_elev_m; + Point3D _tile_center; + + // surface vector heading south + sgVec3 _surface_south; + + // surface vector heading east (used to unambiguously align sky + // with sun) + sgVec3 _surface_east; + + // world up vector (normal to the plane tangent to the earth's + // surface at the spot we are directly above + sgVec3 _world_up; + + // sg versions of our friendly matrices + sgMat4 TRANS, UP; + + ////////////////////////////////////////////////////////////////// + // private functions // + ////////////////////////////////////////////////////////////////// + + void recalc( const Point3D scenery_center ); + void recalcPosition( double lon_deg, double lat_deg, double alt_ft, + const Point3D scenery_center ) const; + + inline void set_dirty() { _dirty = true; } + inline void set_clean() { _dirty = false; } + +}; + + +#endif // _LOCATION_HXX