From 8b13d71fcf20714617ff4864461ecae93c5caec0 Mon Sep 17 00:00:00 2001 From: curt Date: Tue, 19 Dec 2000 21:53:37 +0000 Subject: [PATCH] Rewrite of the property manager my David Megginson. --- simgear/misc/Makefile.am | 5 + simgear/misc/props.cxx | 1680 +++++++++++++++++------------------ simgear/misc/props.hxx | 684 +++++++------- simgear/misc/props_io.cxx | 308 +++---- simgear/misc/props_test.cxx | 338 +++++++ 5 files changed, 1661 insertions(+), 1354 deletions(-) create mode 100644 simgear/misc/props_test.cxx diff --git a/simgear/misc/Makefile.am b/simgear/misc/Makefile.am index 002ffab8..8e91feb7 100644 --- a/simgear/misc/Makefile.am +++ b/simgear/misc/Makefile.am @@ -26,4 +26,9 @@ libsgmisc_a_SOURCES = \ texcoord.cxx \ zfstream.cxx +noinst_PROGRAMS = props_test + +props_test_SOURCES = props_test.cxx props_test.hxx +props_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a + INCLUDES += -I$(top_srcdir) $(ZLIB_INCL) diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx index 428aa467..0e888871 100644 --- a/simgear/misc/props.cxx +++ b/simgear/misc/props.cxx @@ -1,703 +1,768 @@ -// props.cxx -- implementation of SimGear Property Manager. -// -// Written by David Megginson - david@megginson.com -// -// This module is in the PUBLIC DOMAIN. -// -// 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. +// 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$ -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - +#include +#include +#include +#include #include "props.hxx" -#include +using std::cerr; +using std::endl; +using std::sort; -#include -using std::string; + +//////////////////////////////////////////////////////////////////////// +// Convenience macros for value access. +//////////////////////////////////////////////////////////////////////// -SGPropertyList current_properties; +#define GET_BOOL (_value.bool_val->getValue()) +#define GET_INT (_value.int_val->getValue()) +#define GET_FLOAT (_value.float_val->getValue()) +#define GET_DOUBLE (_value.double_val->getValue()) +#define GET_STRING (_value.string_val->getValue()) -static string empty_string; +#define SET_BOOL(val) (_value.bool_val->setValue(val)) +#define SET_INT(val) (_value.int_val->setValue(val)) +#define SET_FLOAT(val) (_value.float_val->setValue(val)) +#define SET_DOUBLE(val) (_value.double_val->setValue(val)) +#define SET_STRING(val) (_value.string_val->setValue(val)) //////////////////////////////////////////////////////////////////////// -// Implementation of SGValue. +// Default values for every type. //////////////////////////////////////////////////////////////////////// +const bool SGRawValue::DefaultValue = false; +const int SGRawValue::DefaultValue = 0; +const float SGRawValue::DefaultValue = 0.0; +const double SGRawValue::DefaultValue = 0.0L; +const string SGRawValue::DefaultValue = ""; -/** - * Construct a new value. - */ -SGValue::SGValue () - : _type(UNKNOWN), _tied(false) -{ -} + +//////////////////////////////////////////////////////////////////////// +// Local path normalization code. +//////////////////////////////////////////////////////////////////////// /** - * Destroy a value. + * A component in a path. */ -SGValue::~SGValue () +struct PathComponent { -} - + string name; + int index; +}; /** - * Return a raw boolean value (no type coercion). + * Parse the name for a path component. + * + * Name: [_a-zA-Z][-._a-zA-Z0-9]* */ -bool -SGValue::getRawBool () const +static inline string +parse_name (const string &path, int &i) { - if (_tied) { - if (_value.bool_func.getter != 0) - return (*(_value.bool_func.getter))(); - else - return false; - } else { - return _value.bool_val; + 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; } /** - * Return a raw integer value (no type coercion). + * Parse the optional integer index for a path component. + * + * Index: "[" [0-9]+ "]" */ -int -SGValue::getRawInt () const +static inline int +parse_index (const string &path, int &i) { - if (_tied) { - if (_value.int_func.getter != 0) - return (*(_value.int_func.getter))(); - else - return 0; - } else { - return _value.int_val; + 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 ']')"); } /** - * Return a raw floating-point value (no type coercion). + * Parse a single path component. + * + * Component: Name Index? */ -float -SGValue::getRawFloat () const +static inline PathComponent +parse_component (const string &path, int &i) { - if (_tied) { - if (_value.float_func.getter != 0) - return (*(_value.float_func.getter))(); - else - return 0.0; - } else { - return _value.float_val; - } + PathComponent component; + component.name = parse_name(path, i); + if (component.name[0] != '.') + component.index = parse_index(path, i); + else + component.index = -1; + return component; } /** - * Return a raw double-precision floating-point value (no type coercion). + * Parse a path into its components. */ -double -SGValue::getRawDouble () const +static void +parse_path (const string &path, vector &components) { - if (_tied) { - if (_value.double_func.getter != 0) - return (*(_value.double_func.getter))(); - else - return 0.0L; - } else { - return _value.double_val; + 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. +//////////////////////////////////////////////////////////////////////// + + /** - * Return a raw string value (no type coercion). + * Locate a child node by name and index. */ -const string & -SGValue::getRawString () const +static int +find_child (const string &name, int index, vector nodes) { - if (_tied && _value.string_func.getter != 0) - return (*(_value.string_func.getter))(); - else - return string_val; + int nNodes = nodes.size(); + for (int i = 0; i < nNodes; i++) { + SGPropertyNode * node = nodes[i]; + if (node->getName() == name && node->getIndex() == index) + return i; + } + return -1; } /** - * Set a raw boolean value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Locate another node, given a relative path. */ -bool -SGValue::setRawBool (bool value) +static SGPropertyNode * +find_node (SGPropertyNode * current, + const vector &components, + int position, + bool create) { - if (_tied) { - if (_value.bool_func.setter != 0) { - (*_value.bool_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.bool_val = value; - return true; + if (current == 0) { + return 0; + } + + else if (position >= components.size()) { + return current; + } + + else if (components[position].name == "") { + return find_node(current->getRootNode(), components, position + 1, create); + } + + else if (components[position].name == ".") { + return find_node(current, components, position + 1, create); + } + + 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); + } + + else { + SGPropertyNode * child = + current->getChild(components[position].name, + components[position].index, + create); + return find_node(child, components, position + 1, create); } } + +//////////////////////////////////////////////////////////////////////// +// Implementation of SGValue. +//////////////////////////////////////////////////////////////////////// + + /** - * Set a raw integer value (no type coercion). + * Default constructor. * - * Return false if the value could not be set, true otherwise. + * The type will be UNKNOWN and the raw value will be "". */ -bool -SGValue::setRawInt (int value) +SGValue::SGValue () + : _type(UNKNOWN), _tied(false) { - if (_tied) { - if (_value.int_func.setter != 0) { - (*_value.int_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.int_val = value; - return true; - } + _value.string_val = new SGRawValueInternal; } /** - * Set a raw floating-point value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Copy constructor. */ -bool -SGValue::setRawFloat (float value) +SGValue::SGValue (const SGValue &source) { - if (_tied) { - if (_value.float_func.setter != 0) { - (*_value.float_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.float_val = value; - return true; + _type = source._type; + _tied = source._tied; + switch (source._type) { + case BOOL: + _value.bool_val = source._value.bool_val->clone(); + break; + case INT: + _value.int_val = source._value.int_val->clone(); + break; + case FLOAT: + _value.float_val = source._value.float_val->clone(); + break; + case DOUBLE: + _value.double_val = source._value.double_val->clone(); + break; + case STRING: + case UNKNOWN: + _value.string_val = source._value.string_val->clone(); + break; } } /** - * Set a raw double-precision floating-point value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Destructor. */ -bool -SGValue::setRawDouble (double value) +SGValue::~SGValue () { - if (_tied) { - if (_value.double_func.setter != 0) { - (*_value.double_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.double_val = value; - return true; - } + clear_value(); } /** - * Set a raw string value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Delete and clear the current value. */ -bool -SGValue::setRawString (const string &value) +void +SGValue::clear_value () { - if (_tied) { - if (_value.string_func.setter != 0) { - (*_value.string_func.setter)(value); - return true; - } else { - return false; - } - } else { - string_val = value; - return true; + switch (_type) { + case BOOL: + delete _value.bool_val; + _value.bool_val = 0; + break; + case INT: + delete _value.int_val; + _value.int_val = 0; + break; + case FLOAT: + delete _value.float_val; + _value.float_val = 0; + break; + case DOUBLE: + delete _value.double_val; + _value.double_val = 0; + break; + case STRING: + case UNKNOWN: + delete _value.string_val; + _value.string_val = 0; + break; } } /** - * Get the boolean value of a property. - * - * If the native type is not boolean, attempt to coerce it. + * Get a boolean value. */ bool SGValue::getBoolValue () const { switch (_type) { case BOOL: - return getRawBool(); + return GET_BOOL; case INT: - return (getRawInt() == 0 ? false : true); + return GET_INT == 0 ? false : true; case FLOAT: - return (getRawFloat() == 0.0 ? false : true); + return GET_FLOAT == 0.0 ? false : true; case DOUBLE: - return (getRawDouble() == 0.0 ? false : true); - case UNKNOWN: + return GET_DOUBLE == 0.0L ? false : true; case STRING: - return ((getRawString() == "true" || getIntValue() != 0) ? true : false); + case UNKNOWN: + return (GET_STRING == "true" || getDoubleValue() != 0.0L); } - return false; } /** - * Get the integer value of a property. - * - * If the native type is not integer, attempt to coerce it. + * Get an integer value. */ int SGValue::getIntValue () const { switch (_type) { case BOOL: - return getRawBool(); + return (int)GET_BOOL; case INT: - return getRawInt(); + return GET_INT; case FLOAT: - return (int)(getRawFloat()); + return (int)GET_FLOAT; case DOUBLE: - return (int)(getRawDouble()); - case UNKNOWN: + return (int)GET_DOUBLE; case STRING: - return atoi(getRawString().c_str()); + case UNKNOWN: + return atoi(GET_STRING.c_str()); } - return false; } /** - * Get the floating-point value of a property. - * - * If the native type is not float, attempt to coerce it. + * Get a float value. */ float SGValue::getFloatValue () const { switch (_type) { case BOOL: - return (float)(getRawBool()); + return (float)GET_BOOL; case INT: - return (float)(getRawInt()); + return (float)GET_INT; case FLOAT: - return getRawFloat(); + return GET_FLOAT; case DOUBLE: - return (float)(getRawDouble()); - case UNKNOWN: + return GET_DOUBLE; case STRING: - return (float)atof(getRawString().c_str()); + case UNKNOWN: + return atof(GET_STRING.c_str()); } - return false; } /** - * Get the double-precision floating-point value of a property. - * - * If the native type is not double, attempt to coerce it. + * Get a double value. */ double SGValue::getDoubleValue () const { switch (_type) { case BOOL: - return (double)(getRawBool()); + return (double)GET_BOOL; case INT: - return (double)(getRawInt()); + return (double)GET_INT; case FLOAT: - return (double)(getRawFloat()); + return (double)GET_FLOAT; case DOUBLE: - return getRawDouble(); - case UNKNOWN: + return GET_DOUBLE; case STRING: - return atof(getRawString().c_str()); + case UNKNOWN: + return (double)atof(GET_STRING.c_str()); } - return false; } /** - * Get the string value of a property. - * - * If the native type is not string, attempt to coerce it. + * Get a string value. */ -const string & +string SGValue::getStringValue () const { - char buf[512]; + char buf[128]; + switch (_type) { case BOOL: - if (getRawBool()) - string_val = "true"; + if (GET_BOOL) + return "true"; else - string_val = "false"; - return string_val; + return "false"; case INT: - sprintf(buf, "%d", getRawInt()); - string_val = buf; - return string_val; + sprintf(buf, "%d", GET_INT); + return buf; case FLOAT: - sprintf(buf, "%f", getRawFloat()); - string_val = buf; - return string_val; + sprintf(buf, "%f", GET_FLOAT); + return buf; case DOUBLE: - sprintf(buf, "%f", getRawDouble()); - string_val = buf; - return string_val; - case UNKNOWN: + sprintf(buf, "%lf", GET_DOUBLE); + return buf; case STRING: - return getRawString(); + case UNKNOWN: + return GET_STRING; } - return empty_string; } /** - * Set the boolean value and change the type if unknown. - * - * Returns true on success. + * Set a bool value. */ bool SGValue::setBoolValue (bool value) { - if (_type == UNKNOWN) - _type = INT; + if (_type == UNKNOWN) { + clear_value(); + _value.bool_val = new SGRawValueInternal; + _type = BOOL; + } + switch (_type) { case BOOL: - return setRawBool(value); + return SET_BOOL(value); case INT: - return setRawInt((int)value); + return SET_INT((int)value); case FLOAT: - return setRawFloat((float)value); + return SET_FLOAT((float)value); case DOUBLE: - return setRawDouble((double)value); + return SET_DOUBLE((double)value); case STRING: - if (value) - return setRawString("true"); - else - return setRawString("false"); + return SET_STRING(value ? "true" : "false"); } + return false; } /** - * Set the integer value and change the type if unknown. - * - * Returns true on success. + * Set an int value. */ bool SGValue::setIntValue (int value) { - if (_type == UNKNOWN) + if (_type == UNKNOWN) { + clear_value(); + _value.int_val = new SGRawValueInternal; _type = INT; + } + switch (_type) { case BOOL: - if (value == 0) - return setRawBool(false); - else - return setRawBool(true); + return SET_BOOL(value == 0 ? false : true); case INT: - return setRawInt(value); + return SET_INT(value); case FLOAT: - return setRawFloat((float)value); + return SET_FLOAT((float)value); case DOUBLE: - return setRawDouble((double)value); - case STRING: + return SET_DOUBLE((double)value); + case STRING: { char buf[128]; sprintf(buf, "%d", value); - return setRawString(buf); + return SET_STRING(buf); + } } + return false; } /** - * Set the floating-point value and change the type if unknown. - * - * Returns true on success. + * Set a float value. */ bool SGValue::setFloatValue (float value) { - if (_type == UNKNOWN) + if (_type == UNKNOWN) { + clear_value(); + _value.float_val = new SGRawValueInternal; _type = FLOAT; + } + switch (_type) { case BOOL: - if (value == 0.0) - return setRawBool(false); - else - return setRawBool(true); + return SET_BOOL(value == 0.0 ? false : true); case INT: - return setRawInt((int)value); + return SET_INT((int)value); case FLOAT: - return setRawFloat(value); + return SET_FLOAT(value); case DOUBLE: - return setRawDouble((double)value); - case STRING: + return SET_DOUBLE((double)value); + case STRING: { char buf[128]; sprintf(buf, "%f", value); - return setRawString(buf); + return SET_STRING(buf); } + } + return false; } /** - * Set the double-precision value and change the type if unknown. - * - * Returns true on success. + * Set a double value. */ bool SGValue::setDoubleValue (double value) { - if (_type == UNKNOWN) + if (_type == UNKNOWN) { + clear_value(); + _value.double_val = new SGRawValueInternal; _type = DOUBLE; + } + switch (_type) { case BOOL: - if (value == 0.0L) - return setRawBool(false); - else - return setRawBool(true); + return SET_BOOL(value == 0.0L ? false : true); case INT: - return setRawInt((int)value); + return SET_INT((int)value); case FLOAT: - return setRawFloat((float)value); + return SET_FLOAT((float)value); case DOUBLE: - return setRawDouble(value); - case STRING: + return SET_DOUBLE(value); + case STRING: { char buf[128]; sprintf(buf, "%lf", value); - return setRawString(buf); + return SET_STRING(buf); } + } + return false; } /** - * Set the string value and change the type if unknown. - * - * Returns true on success. + * Set a string value. */ bool -SGValue::setStringValue (const string &value) +SGValue::setStringValue (string value) { - if (_type == UNKNOWN) + if (_type == UNKNOWN) { + clear_value(); + _value.string_val = new SGRawValueInternal; _type = STRING; + } switch (_type) { case BOOL: - if (value == "true" || atoi(value.c_str()) != 0) - return setRawBool(true); - else - return setRawBool(false); + return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false); case INT: - return setRawInt(atoi(value.c_str())); + return SET_INT(atoi(value.c_str())); case FLOAT: - return setRawFloat(atof(value.c_str())); + return SET_FLOAT(atof(value.c_str())); case DOUBLE: - return setRawDouble(atof(value.c_str())); + return SET_DOUBLE((double)atof(value.c_str())); case STRING: - return setRawString(value); + return SET_STRING(value); } + return false; } /** - * Set a string value and don't modify the type. - * - * Returns true on success. + * Set a value of unknown type (stored as a string). */ bool -SGValue::setUnknownValue (const string &value) +SGValue::setUnknownValue (string value) { switch (_type) { case BOOL: - if (value == "true" || atoi(value.c_str()) != 0) - return setRawBool(true); - else - return setRawBool(false); + return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false); case INT: - return setRawInt(atoi(value.c_str())); + return SET_INT(atoi(value.c_str())); case FLOAT: - return setRawFloat(atof(value.c_str())); + return SET_FLOAT(atof(value.c_str())); case DOUBLE: - return setRawDouble(atof(value.c_str())); + return SET_DOUBLE((double)atof(value.c_str())); case STRING: case UNKNOWN: - return setRawString(value); + return SET_STRING(value); } + return false; } /** - * Tie a boolean value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). + * Tie a bool value. */ bool -SGValue::tieBool (bool_getter getter, bool_setter setter, - bool useDefault) +SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) { + if (_tied) return false; - } else { - if (useDefault && setter) - (*setter)(getBoolValue()); - _tied = true; - _type = BOOL; - _value.bool_func.getter = getter; - _value.bool_func.setter = setter; - return true; - } + + bool old_val; + if (use_default) + old_val = getBoolValue(); + + clear_value(); + _type = BOOL; + _tied = true; + _value.bool_val = value.clone(); + + if (use_default) + setBoolValue(old_val); + + return true; } /** - * Tie an integer value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). + * Tie an int value. */ bool -SGValue::tieInt (int_getter getter, int_setter setter, - bool useDefault) +SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) { + if (_tied) return false; - } else { - if (useDefault && setter) - (*setter)(getIntValue()); - _tied = true; - _type = INT; - _value.int_func.getter = getter; - _value.int_func.setter = setter; - return true; - } + + int old_val; + if (use_default) + old_val = getIntValue(); + + clear_value(); + _type = INT; + _tied = true; + _value.int_val = value.clone(); + + if (use_default) + setIntValue(old_val); + + return true; } /** - * Tie a floating-point value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). + * Tie a float value. */ bool -SGValue::tieFloat (float_getter getter, float_setter setter, - bool useDefault) +SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) { + if (_tied) return false; - } else { - if (useDefault && setter) - (*setter)(getFloatValue()); - _tied = true; - _type = FLOAT; - _value.float_func.getter = getter; - _value.float_func.setter = setter; - return true; - } + + float old_val; + if (use_default) + old_val = getFloatValue(); + + clear_value(); + _type = FLOAT; + _tied = true; + _value.float_val = value.clone(); + + if (use_default) + setFloatValue(old_val); + + return true; } /** - * Tie a double-precision floating-point value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). + * Tie a double value. */ bool -SGValue::tieDouble (double_getter getter, double_setter setter, - bool useDefault) +SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) { + if (_tied) return false; - } else { - if (useDefault && setter) - (*setter)(getDoubleValue()); - _tied = true; - _type = DOUBLE; - _value.double_func.getter = getter; - _value.double_func.setter = setter; - return true; - } + + double old_val; + if (use_default) + old_val = getDoubleValue(); + + clear_value(); + _type = DOUBLE; + _tied = true; + _value.double_val = value.clone(); + + if (use_default) + setDoubleValue(old_val); + + return true; } /** - * Tie a string value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). + * Tie a string value. */ bool -SGValue::tieString (string_getter getter, string_setter setter, - bool useDefault) +SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) { + if (_tied) return false; - } else { - if (useDefault && setter) - (*setter)(getStringValue()); - _tied = true; - _type = STRING; - _value.string_func.getter = getter; - _value.string_func.setter = setter; - return true; - } + + string old_val; + if (use_default) + old_val = getStringValue(); + + clear_value(); + _type = STRING; + _tied = true; + _value.string_val = value.clone(); + + if (use_default) + setStringValue(old_val); + + return true; } /** - * Untie a value from external functions. - * - * Will always attempt to intialize the internal value from - * the getter before untying. - * - * Returns true on success (i.e. the value had been tied). + * Untie a value. */ bool SGValue::untie () @@ -707,717 +772,604 @@ SGValue::untie () switch (_type) { case BOOL: { - bool value = getRawBool(); - _tied = false; - setRawBool(value); + bool val = getBoolValue(); + clear_value(); + _value.bool_val = new SGRawValueInternal; + SET_BOOL(val); break; } case INT: { - int value = getRawInt(); - _tied = false; - setRawInt(value); + int val = getIntValue(); + clear_value(); + _value.int_val = new SGRawValueInternal; + SET_INT(val); break; } case FLOAT: { - float value = getRawFloat(); - _tied = false; - setRawFloat(value); + float val = getFloatValue(); + clear_value(); + _value.float_val = new SGRawValueInternal; + SET_FLOAT(val); break; } case DOUBLE: { - double value = getRawDouble(); - _tied = false; - setRawDouble(value); + double val = getDoubleValue(); + clear_value(); + _value.double_val = new SGRawValueInternal; + SET_DOUBLE(val); break; } case STRING: { - string value = getRawString(); - _tied = false; - setRawString(value); + string val = getStringValue(); + clear_value(); + _value.string_val = new SGRawValueInternal; + SET_STRING(val); break; } } + _tied = false; return true; } //////////////////////////////////////////////////////////////////////// -// Implementation of SGPropertyList. +// Implementation of SGPropertyNode. //////////////////////////////////////////////////////////////////////// /** - * Constructor. + * Default constructor: always creates a root node. */ -SGPropertyList::SGPropertyList () +SGPropertyNode::SGPropertyNode () + : _value(0), _name(""), _index(0), _parent(0) { } /** - * Destructor. + * Convenience constructor. */ -SGPropertyList::~SGPropertyList () +SGPropertyNode::SGPropertyNode (const string &name, + int index, SGPropertyNode * parent) + : _value(0), _name(name), _index(index), _parent(parent) { } +SGPropertyNode::~SGPropertyNode () +{ + delete _value; + for (int i = 0; i < _children.size(); i++) + delete _children[i]; +} -/** - * Return true if a value is present. - */ -bool -SGPropertyList::hasValue (const string &name) const +SGPropertyNode * +SGPropertyNode::getChild (int position) { - const_iterator el = _props.find(name); - if (el == _props.end()) - return false; + if (position >= 0 && position < nChildren()) + return _children[position]; else - return true; + return 0; } +const SGPropertyNode * +SGPropertyNode::getChild (int position) const +{ + if (position >= 0 && position < nChildren()) + return _children[position]; + else + return 0; +} -/** - * Look up the SGValue structure associated with a property. - * - * Run some basic validity checks on the property name: it must - * not be empty, must begin with '/', must never have two '//' in a row, - * and must not end with '/'. - */ -SGValue * -SGPropertyList::getValue (const string &name, bool create) -{ - const_iterator el = _props.find(name); - if (el == _props.end()) { - if (!create) - return 0; - else { - FG_LOG(FG_GENERAL, FG_DEBUG, "Creating new property '" << name << '\''); - if (name.size() == 0 || - name[0] != '/' || - name[name.size()-1] == '/' || - name.find("//") != string::npos) { - FG_LOG(FG_GENERAL, FG_ALERT, "Illegal property name: '" - << name << '\''); - return 0; - } - } +SGPropertyNode * +SGPropertyNode::getChild (const string &name, int index, bool create) +{ + int pos = find_child(name, index, _children); + if (pos >= 0) { + return getChild(pos); + } else if (create) { + _children.push_back(new SGPropertyNode(name, index, this)); + return _children[_children.size()-1]; + } else { + return 0; } - return &(_props[name]); } - -/** - * Look up a const value (never created). - */ -const SGValue * -SGPropertyList::getValue (const string &name) const +const SGPropertyNode * +SGPropertyNode::getChild (const string &name, int index) const { - value_map::const_iterator el = _props.find(name); - if (el == _props.end()) - return 0; + int pos = find_child(name, index, _children); + if (pos >= 0) + _children[_children.size()-1]; else - return &(el->second); + return 0; } +class CompareIndices +{ +public: + int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const { + return (n1->getIndex() < n2->getIndex()); + } +}; + + /** - * Extract a boolean from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. + * Get all children with the same name (but different indices). */ -bool -SGPropertyList::getBoolValue (const string &name, bool defaultValue) const +vector +SGPropertyNode::getChildren (const string &name) { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getBoolValue(); + vector children; + int max = _children.size(); + + for (int i = 0; i < max; i++) + if (_children[i]->getName() == name) + children.push_back(_children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; } /** - * Extract an integer from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. + * Get all children with the same name (but different indices). */ -int -SGPropertyList::getIntValue (const string &name, int defaultValue) const +vector +SGPropertyNode::getChildren (const string &name) const { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getIntValue(); + vector children; + int max = _children.size(); + + for (int i = 0; i < max; i++) + if (_children[i]->getName() == name) + children.push_back(_children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; } -/** - * Extract a float from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. - */ -float -SGPropertyList::getFloatValue (const string &name, float defaultValue) const +string +SGPropertyNode::getPath (bool simplify) const { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; + if (_parent == 0) + return ""; + + string path = _parent->getPath(simplify); + path += '/'; + path += _name; + if (_index != 0 || !simplify) { + char buffer[128]; + sprintf(buffer, "[%d]", _index); + path += buffer; + } + return path; +} + +SGValue::Type +SGPropertyNode::getType () const +{ + if (_value != 0) + return _value->getType(); else - return val->getFloatValue(); + return SGValue::UNKNOWN; } +bool +SGPropertyNode::getBoolValue () const +{ + return (_value == 0 ? SGRawValue::DefaultValue + : _value->getBoolValue()); +} -/** - * Extract a double from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. - */ -double -SGPropertyList::getDoubleValue (const string &name, double defaultValue) const +int +SGPropertyNode::getIntValue () const { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getDoubleValue(); + return (_value == 0 ? SGRawValue::DefaultValue + : _value->getIntValue()); } +float +SGPropertyNode::getFloatValue () const +{ + return (_value == 0 ? SGRawValue::DefaultValue + : _value->getFloatValue()); +} -/** - * Extract a string from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and query it repeatedly. - */ -const string & -SGPropertyList::getStringValue (const string &name, - const string &defaultValue) const +double +SGPropertyNode::getDoubleValue () const { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getStringValue(); + return (_value == 0 ? SGRawValue::DefaultValue + : _value->getDoubleValue()); } +string +SGPropertyNode::getStringValue () const +{ + return (_value == 0 ? SGRawValue::DefaultValue + : _value->getStringValue()); +} -/** - * Assign a bool to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and modify it repeatedly. - * - * Returns true on success. - */ bool -SGPropertyList::setBoolValue (const string &name, bool value) +SGPropertyNode::setBoolValue (bool val) { - return getValue(name, true)->setBoolValue(value); + if (_value == 0) + _value = new SGValue(); + return _value->setBoolValue(val); } +bool +SGPropertyNode::setIntValue (int val) +{ + if (_value == 0) + _value = new SGValue(); + return _value->setIntValue(val); +} -/** - * Assign an integer to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and modify it repeatedly. - * - * Returns true on success. - */ bool -SGPropertyList::setIntValue (const string &name, int value) +SGPropertyNode::setFloatValue (float val) { - return getValue(name, true)->setIntValue(value); + if (_value == 0) + _value = new SGValue(); + return _value->setFloatValue(val); } +bool +SGPropertyNode::setDoubleValue (double val) +{ + if (_value == 0) + _value = new SGValue(); + return _value->setDoubleValue(val); +} -/** - * Assign a float to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and modify it repeatedly. - * - * Returns true on success. - */ bool -SGPropertyList::setFloatValue (const string &name, float value) +SGPropertyNode::setStringValue (string val) { - return getValue(name, true)->setFloatValue(value); + if (_value == 0) + _value = new SGValue(); + return _value->setStringValue(val); } +bool +SGPropertyNode::setUnknownValue (string val) +{ + if (_value == 0) + _value = new SGValue(); + return _value->setUnknownValue(val); +} -/** - * Assign a double to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and modify it repeatedly. - * - * Returns true on success. - */ bool -SGPropertyList::setDoubleValue (const string &name, double value) +SGPropertyNode::isTied () const { - return getValue(name, true)->setDoubleValue(value); + return (_value == 0 ? false : _value->isTied()); } +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + return (_value == 0 ? false : _value->tie(rawValue, useDefault)); +} -/** - * Assign a string to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and modify it repeatedly. - * - * Returns true on success. - */ bool -SGPropertyList::setStringValue (const string &name, const string &value) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - return getValue(name, true)->setStringValue(value); + return (_value == 0 ? false : _value->tie(rawValue, useDefault)); } +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + return (_value == 0 ? false : _value->tie(rawValue, useDefault)); +} -/** - * Assign a string to the value, but don't change the type. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and modify it repeatedly. - * - * Returns true on success. - */ bool -SGPropertyList::setUnknownValue (const string &name, const string &value) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - return getValue(name, true)->setUnknownValue(value); + return (_value == 0 ? false : _value->tie(rawValue, useDefault)); } +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + return (_value == 0 ? false : _value->tie(rawValue, useDefault)); +} -/** - * Tie a boolean value to external functions. - * - * Invokes SGValue::tieBool - */ bool -SGPropertyList::tieBool (const string &name, - bool_getter getter, - bool_setter setter, - bool useDefault) +SGPropertyNode::untie () +{ + return (_value == 0 ? false : _value->untie()); +} + +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 string &relative_path, bool create) +{ + vector components; + parse_path(relative_path, components); + return find_node(this, components, 0, create); +} + +const SGPropertyNode * +SGPropertyNode::getNode (const string &relative_path) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying bool property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieBool(getter, setter, useDefault); + vector components; + parse_path(relative_path, components); + // FIXME: cast away const + return find_node((SGPropertyNode *)this, components, 0, false); } + +//////////////////////////////////////////////////////////////////////// +// Convenience methods using relative paths. +//////////////////////////////////////////////////////////////////////// + + /** - * Tie an integer value to external functions. - * - * Invokes SGValue::tieInt + * Test whether another node has a value attached. */ bool -SGPropertyList::tieInt (const string &name, - int_getter getter, - int_setter setter, - bool useDefault) +SGPropertyNode::hasValue (const string &relative_path) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying int property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieInt(getter, setter, useDefault); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->hasValue()); } /** - * Tie a float value to external functions. - * - * Invokes SGValue::tieFloat + * Get the value for another node. */ -bool -SGPropertyList::tieFloat (const string &name, - float_getter getter, - float_setter setter, - bool useDefault) +SGValue * +SGPropertyNode::getValue (const string &relative_path, bool create) { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying float property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieFloat(getter, setter, useDefault); + SGPropertyNode * node = getNode(relative_path, create); + if (node != 0 && !node->hasValue()) + node->setUnknownValue(""); + return (node == 0 ? 0 : node->getValue()); } /** - * Tie a double value to external functions. - * - * Invokes SGValue::tieDouble + * Get the value for another node. */ -bool -SGPropertyList::tieDouble (const string &name, - double_getter getter, - double_setter setter, - bool useDefault) +const SGValue * +SGPropertyNode::getValue (const string &relative_path) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying double property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieDouble(getter, setter, useDefault); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? 0 : node->getValue()); } /** - * Tie a string value to external functions. - * - * Invokes SGValue::tieString + * Get the value type for another node. */ -bool -SGPropertyList::tieString (const string &name, - string_getter getter, - string_setter setter, - bool useDefault) +SGValue::Type +SGPropertyNode::getType (const string &relative_path) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying string property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieString(getter, setter, useDefault); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? SGValue::UNKNOWN : node->getType()); } /** - * Untie a value from external functions. - * - * Invokes SGValue::untie + * Get a bool value for another node. */ bool -SGPropertyList::untie (const string &name) +SGPropertyNode::getBoolValue (const string &relative_path, + bool defaultValue) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Untying property '" << name << '\''); - return getValue(name, true)->untie(); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getBoolValue()); } - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGPropertyNode. -//////////////////////////////////////////////////////////////////////// - - /** - * Extract the base name of the next level down from the parent. - * - * The parent must have a '/' appended. Note that basename may - * be modified even if the test fails. + * Get an int value for another node. */ -static bool -get_base (const string &parent, const string &child, - string &basename) -{ - // First, check that the parent name - // is a prefix of the child name, and - // extract the remainder - if (child.find(parent) != 0) - return false; - - basename = child.substr(parent.size()); - - string::size_type pos = basename.find('/'); - if (pos != string::npos) { - basename.resize(pos); - } - - if (basename.size() == 0) - return false; - else - return true; +int +SGPropertyNode::getIntValue (const string &relative_path, + int defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getIntValue()); } /** - * Constructor. + * Get a float value for another node. */ -SGPropertyNode::SGPropertyNode (const string &path, - SGPropertyList * props) - : _props(props), _node(0) +float +SGPropertyNode::getFloatValue (const string &relative_path, + float defaultValue) const { - setPath(path); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getFloatValue()); } /** - * Destructor. + * Get a double value for another node. */ -SGPropertyNode::~SGPropertyNode () +double +SGPropertyNode::getDoubleValue (const string &relative_path, + double defaultValue) const { - delete _node; - _node = 0; + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getDoubleValue()); } /** - * Set the path. - * - * Strip the trailing '/', if any. + * Get a string value for another node. */ -void -SGPropertyNode::setPath (const string &path) +string +SGPropertyNode::getStringValue (const string &relative_path, + string defaultValue) const { - _path = path; - - // Chop the final '/', if present. - if (_path.size() > 0 && _path[_path.size()-1] == '/') - _path.resize(_path.size()-1); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getStringValue()); } /** - * Return the local name of the property. - * - * The local name is just everything after the last slash. + * Set a bool value for another node. */ -const string & -SGPropertyNode::getName () const +bool +SGPropertyNode::setBoolValue (const string &relative_path, bool value) { - string::size_type pos = _path.rfind('/'); - if (pos != string::npos) { - _name = _path.substr(pos+1); - return _name; - } else { - return empty_string; - } + return getNode(relative_path, true)->setBoolValue(value); } /** - * Return the number of children for the current node. + * Set an int value for another node. */ -int -SGPropertyNode::size () const +bool +SGPropertyNode::setIntValue (const string &relative_path, int value) { - if (_props == 0) - return 0; - - int s = 0; - - string base; - string lastBase; - string pattern = _path; - pattern += '/'; + return getNode(relative_path, true)->setIntValue(value); +} - SGPropertyList::const_iterator it = _props->begin(); - SGPropertyList::const_iterator end = _props->end(); - while (it != end) { - if (get_base(pattern, it->first, base) && base != lastBase) { - s++; - lastBase = base; - } - it++; - } - return s; +/** + * Set a float value for another node. + */ +bool +SGPropertyNode::setFloatValue (const string &relative_path, float value) +{ + return getNode(relative_path, true)->setFloatValue(value); } /** - * Initialize a node to represent this node's parent. - * - * A return value of true means success; otherwise, the node supplied - * is unmodified. + * Set a double value for another node. */ -SGPropertyNode & -SGPropertyNode::getParent () const +bool +SGPropertyNode::setDoubleValue (const string &relative_path, double value) { - if (_node == 0) - _node = new SGPropertyNode(); - - string::size_type pos = _path.rfind('/'); - if (pos != string::npos) { - _node->setPropertyList(_props); - _node->setPath(_path.substr(0, pos-1)); - } - return *_node; + return getNode(relative_path, true)->setDoubleValue(value); } /** - * Initialize a node to represent this node's nth child. - * - * A return value of true means success; otherwise, the node supplied - * is unmodified. + * Set a string value for another node. */ -SGPropertyNode & -SGPropertyNode::getChild (int n) const -{ - if (_node == 0) - _node = new SGPropertyNode(); - - if (_props == 0) - return *_node; - - int s = 0; - string base; - string lastBase; - string pattern = _path; - pattern += '/'; - - SGPropertyList::const_iterator it = _props->begin(); - SGPropertyList::const_iterator end = _props->end(); - while (it != end) { - if (get_base(pattern, it->first, base) && base != lastBase) { - if (s == n) { - _node->setPropertyList(_props); - _node->setPath(_path + string("/") + base); - return *_node; - } else { - s++; - lastBase = base; - } - } - it++; - } - - return *_node; +bool +SGPropertyNode::setStringValue (const string &relative_path, string value) +{ + return getNode(relative_path, true)->setStringValue(value); } /** - * Return a node for an arbitrary subpath. - * - * Never returns 0. + * Set an unknown value for another node. */ -SGPropertyNode & -SGPropertyNode::getSubNode (const string &subpath) const +bool +SGPropertyNode::setUnknownValue (const string &relative_path, string value) { - if (_node == 0) - _node = new SGPropertyNode(); - - _node->setPropertyList(_props); - _node->setPath(_path + string("/") + subpath); - return *_node; + return getNode(relative_path, true)->setUnknownValue(value); } /** - * Test whether the specified subpath has a value. + * Test whether another node is tied. */ bool -SGPropertyNode::hasValue (const string &subpath) const +SGPropertyNode::isTied (const string &relative_path) const { - if (_props == 0) - return false; - - if (subpath.size() == 0) - return _props->hasValue(_path); - else - return _props->hasValue(_path + string("/") + subpath); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->isTied()); } /** - * Return the value of the current node. - * - * Currently, this does a lookup each time, but we could cache the - * value safely as long as it's non-zero. - * - * Note that this will not create the value if it doesn't already exist. + * Tie a node reached by a relative path, creating it if necessary. */ -SGValue * -SGPropertyNode::getValue (const string &subpath) +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault = true) { - if (_props == 0) - return 0; - - if (subpath.size() == 0) - return _props->getValue(_path); - else - return _props->getValue(_path + string("/") + subpath); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a bool value. + * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::getBoolValue (const string &subpath, bool defaultValue) const +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault = true) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getBoolValue(_path, defaultValue); - else - return _props->getBoolValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return an int value. + * Tie a node reached by a relative path, creating it if necessary. */ -int -SGPropertyNode::getIntValue (const string &subpath, int defaultValue) const +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault = true) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getIntValue(_path, defaultValue); - else - return _props->getIntValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a float value. + * Tie a node reached by a relative path, creating it if necessary. */ -float -SGPropertyNode::getFloatValue (const string &subpath, float defaultValue) const +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault = true) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getFloatValue(_path, defaultValue); - else - return _props->getFloatValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a double value. + * Tie a node reached by a relative path, creating it if necessary. */ -double -SGPropertyNode::getDoubleValue (const string &subpath, - double defaultValue) const +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault = true) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getDoubleValue(_path, defaultValue); - else - return _props->getDoubleValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a string value. + * Attempt to untie another node reached by a relative path. */ -const string & -SGPropertyNode::getStringValue (const string &subpath, - const string &defaultValue) const +bool +SGPropertyNode::untie (const string &relative_path) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getStringValue(_path, defaultValue); - else - return _props->getStringValue(_path + string("/") + subpath, - defaultValue); + SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->untie()); } - // end of props.cxx diff --git a/simgear/misc/props.hxx b/simgear/misc/props.hxx index d50d3958..f8d00738 100644 --- a/simgear/misc/props.hxx +++ b/simgear/misc/props.hxx @@ -1,12 +1,6 @@ -// props.hxx -- declaration of SimGear Property Manager. -// -// Written by David Megginson - david@megginson.com -// -// This module is in the PUBLIC DOMAIN. -// -// 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. +// 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]. // @@ -18,11 +12,11 @@ #include #include -#include +#include #include using std::string; -using std::map; +using std::vector; using std::istream; using std::ostream; @@ -59,370 +53,426 @@ using std::ostream; //////////////////////////////////////////////////////////////////////// -// Values. +// 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. //////////////////////////////////////////////////////////////////////// + /** - * Abstract representation of a FlightGear value. + * Abstract base class for a raw value. + * + * Unlike values, raw values are not persistent -- the raw value can + * change frequently, but the changes are not visible to the application. * - * This value is designed to be fairly robust -- it can exist without - * a specified value, it can be any of several types, and it can - * be tied to an external variable without disrupting any existing - * pointers or references to the value. Some basic type conversions - * are also handled automatically. + * The SGValue 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 clone() method, strange things will happen. * - * Values also have attributes that control whether they can be read - * from, written to, or archived (i.e. saved to disk). + * All raw values must implement getValue, setValue, and clone for the + * appropriate type. */ -class SGValue +template +class SGRawValue { public: + static const T DefaultValue; // Default for this kind of raw value. - // External getters - typedef bool (*bool_getter)(); - typedef int (*int_getter)(); - typedef float (*float_getter)(); - typedef double (*double_getter)(); - typedef const string &(*string_getter)(); - - // External setters - typedef void (*bool_setter)(bool); - typedef void (*int_setter)(int); - typedef void (*float_setter)(float); - typedef void (*double_setter)(double); - typedef void (*string_setter)(const string &); + SGRawValue () {} + virtual ~SGRawValue () {} + virtual T getValue () const = 0; + virtual bool setValue (T value) = 0; + virtual SGRawValue * clone () const = 0; +}; - enum Type { - UNKNOWN, // no value assigned yet - BOOL, // boolean - INT, // integer - FLOAT, // floating point - DOUBLE, // double precision - STRING // text - }; - SGValue (); - virtual ~SGValue (); - - // Meta information. - virtual Type getType () const { return _type; } - virtual bool isTied () const { return _tied; } - - // Accessors. - virtual bool getBoolValue () const; - virtual int getIntValue () const; - virtual float getFloatValue () const; - virtual double getDoubleValue () const; - virtual const string & getStringValue () const; - - // Setters. - virtual bool setBoolValue (bool value); - virtual bool setIntValue (int value); - virtual bool setFloatValue (float value); - virtual bool setDoubleValue (double value); - virtual bool setStringValue (const string &value); - virtual bool setUnknownValue (const string &value); - - // Tie to external variables. - virtual bool tieBool (bool_getter getter, - bool_setter setter = 0, - bool useDefault = true); - virtual bool tieInt (int_getter getter, - int_setter setter = 0, - bool useDefault = true); - virtual bool tieFloat (float_getter getter, - float_setter setter = 0, - bool useDefault = true); - virtual bool tieDouble (double_getter getter, - double_setter setter = 0, - bool useDefault = true); - virtual bool tieString (string_getter getter, - string_setter setter = 0, - bool useDefault = true); - - // Untie from external variables. - virtual bool untie (); +/** + * A value managed internally. + * + * Instances of this class are created automatically, by default, + * by the SGValue class; ordinarily the application should not + * need to touch it. + */ +template +class SGRawValueInternal : public SGRawValue +{ +public: + SGRawValueInternal () {} + SGRawValueInternal (T value) : _value(value) {} + virtual ~SGRawValueInternal () {} + virtual T getValue () const { return _value; } + virtual bool setValue (T value) { _value = value; return true; } + virtual SGRawValue * clone () const { + return new SGRawValueInternal(_value); + } +private: + T _value; +}; -protected: - bool getRawBool () const; - int getRawInt () const; - float getRawFloat () const; - double getRawDouble () const; - const string &getRawString () const; +/** + * A value managed through a direct 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. + */ +template +class SGRawValuePointer : public SGRawValue +{ +public: + SGRawValuePointer (T * ptr) : _ptr(ptr) {} + virtual ~SGRawValuePointer () {} + virtual T getValue () const { return *_ptr; } + virtual bool setValue (T value) { *_ptr = value; return true; } + virtual SGRawValue * clone () const { + return new SGRawValuePointer(_ptr); + } +private: + T * _ptr; +}; - bool setRawBool (bool value); - bool setRawInt (int value); - bool setRawFloat (float value); - bool setRawDouble (double value); - bool setRawString (const string & value); +/** + * 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: + typedef T (*getter_t)(); + typedef void (*setter_t)(T); + SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0) + : _getter(getter), _setter(setter) {} + virtual ~SGRawValueFunctions () {} + virtual T getValue () const { + if (_getter) return (*_getter)(); + else return DefaultValue; + } + virtual bool setValue (T value) { + if (_setter) { (*_setter)(value); return true; } + else return false; + } + virtual SGRawValue * clone () const { + return new SGRawValueFunctions(_getter,_setter); + } private: + getter_t _getter; + setter_t _setter; +}; - Type _type; - bool _tied; - mutable string string_val; +/** + * An indexed 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 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 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; +}; - // The value is one of the following... - union { - bool bool_val; - int int_val; - float float_val; - double double_val; - - struct { - bool_setter setter; - bool_getter getter; - } bool_func; - - struct { - int_setter setter; - int_getter getter; - } int_func; - - struct { - void * obj; - float_setter setter; - float_getter getter; - } float_func; - - struct { - void * obj; - double_setter setter; - double_getter getter; - } double_func; - - struct { - string_setter setter; - string_getter getter; - } string_func; +/** + * 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 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; +}; - } _value; +/** + * 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 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; }; //////////////////////////////////////////////////////////////////////// -// Top-level manager. +// A cooked value. +// +// This is the value that property-list clients see. It is a +// persistent layer over the possibly-changing raw value; once a +// client gets an SGValue from the property manager, the pointer +// will be good for the life of the property manager itself, no +// matter how often the pointer is tied or untied. //////////////////////////////////////////////////////////////////////// - -/** - * A list of FlightGear properties. - * - * This list associates names (conventional written as paths, - * i.e. "/foo/bar/hack") with SGValue classes. Once an SGValue - * object is associated with the name, the association is - * permanent -- it is safe to keep a pointer or reference. - * however, that the type of a value may change if it is tied - * to a variable. - * - * When iterating through the list, the value type is - * - * pair - * - * To get the name from a const_iterator, use - * - * it->first - * - * and to get the value from a const_iterator, use - * - * it->second - */ -class SGPropertyList +class SGValue { public: - typedef map value_map; - - typedef SGValue::bool_getter bool_getter; - typedef SGValue::int_getter int_getter; - typedef SGValue::float_getter float_getter; - typedef SGValue::double_getter double_getter; - typedef SGValue::string_getter string_getter; - - typedef SGValue::bool_setter bool_setter; - typedef SGValue::int_setter int_setter; - typedef SGValue::float_setter float_setter; - typedef SGValue::double_setter double_setter; - typedef SGValue::string_setter string_setter; - - typedef value_map::value_type value_type; - typedef value_map::size_type size_type; - typedef value_map::const_iterator const_iterator; - - SGPropertyList (); - virtual ~SGPropertyList (); - - virtual size_type size () const { return _props.size(); } - - virtual const_iterator begin () const { return _props.begin(); } - virtual const_iterator end () const { return _props.end(); } - - virtual bool hasValue (const string &name) const; - - virtual SGValue * getValue (const string &name, bool create = false); - virtual const SGValue * getValue (const string &name) const; - - virtual bool getBoolValue (const string &name, - bool defaultValue = false) const; - virtual int getIntValue (const string &name, - int defaultValue = 0) const; - virtual float getFloatValue (const string &name, - float defaultValue = 0.0) const; - virtual double getDoubleValue (const string &name, - double defaultValue = 0.0L) const; - virtual const string & getStringValue (const string &name, - const string &defaultValue = "") - const; - - virtual bool setBoolValue (const string &name, bool value); - virtual bool setIntValue (const string &name, int value); - virtual bool setFloatValue (const string &name, float value); - virtual bool setDoubleValue (const string &name, double value); - virtual bool setStringValue (const string &name, const string &value); - virtual bool setUnknownValue (const string &name, const string &value); - - virtual bool tieBool (const string &name, - bool_getter getter, - bool_setter setter = 0, - bool useDefault = true); - virtual bool tieInt (const string &name, - int_getter getter, - int_setter setter = 0, - bool useDefault = true); - virtual bool tieFloat (const string &name, - float_getter getter, - float_setter setter = 0, - bool useDefault = true); - virtual bool tieDouble (const string &name, - double_getter getter, - double_setter setter = 0, - bool useDefault = true); - virtual bool tieString (const string &name, - string_getter getter, - string_setter setter = 0, - bool useDefault = true); - - virtual bool untie (const string &name); + enum Type { + BOOL, + INT, + FLOAT, + DOUBLE, + STRING, + UNKNOWN + }; + SGValue (); + SGValue (const SGValue &value); + ~SGValue (); + + Type getType () const { return _type; } + + bool getBoolValue () const; + int getIntValue () const; + float getFloatValue () const; + double getDoubleValue () const; + string getStringValue () const; + + bool setBoolValue (bool value); + bool setIntValue (int value); + bool setFloatValue (float value); + bool setDoubleValue (double value); + bool setStringValue (string value); + bool setUnknownValue (string value); + + bool isTied () const { return _tied; } + + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + bool untie (); private: - value_map _props; + + void clear_value (); + + Type _type; + bool _tied; + + // The right kind of pointer... + union { + SGRawValue * bool_val; + SGRawValue * int_val; + SGRawValue * float_val; + SGRawValue * double_val; + SGRawValue * string_val; + } _value; + }; //////////////////////////////////////////////////////////////////////// -// Tree/node/directory view. +// A node in a property tree. //////////////////////////////////////////////////////////////////////// - -/** - * Tree view of a property list. - * - * This class provides a virtual tree view of a property list, without - * actually constructing a tree -- the view always stays in sync with - * the property list itself. - * - * This class is designed to be used for setup and configuration; it is - * optimized for ease of use rather than performance, and shouldn't be - * used inside a tight loop. - * - * Every node is actually just a path together with a pointer to - * the real property list and a few convenient operations; to the - * user, however, it looks like a node in a tree or a file system, - * with the regular operations such as getChild and getParent. - * - * Note that a node may be both a branch and a leaf -- that is, it - * may have a value itself and it may have children. Here is a simple - * example that prints the names of all of the different nodes inside - * "/controls": - * - * SGPropertyNode controls("/controls", current_property_list); - * SGPropertyNode child; - * int size = controls.size(); - * for (int i = 0; i < size; i++) { - * if (controls.getChild(child, i)) - * cout << child.getName() << endl; - * else - * cerr << "Failed to read child " << i << endl; - * } - */ class SGPropertyNode { + public: - // Constructor and destructor - SGPropertyNode (const string &path = "", SGPropertyList * props = 0); + + SGPropertyNode (); virtual ~SGPropertyNode (); - // Accessor and setter for the internal - // path. - virtual const string &getPath () const { return _path; } - virtual void setPath (const string &path); + // Basic properties. + bool hasValue () const { return (_value != 0); } + SGValue * getValue () { return _value; } + const SGValue * getValue () const { return _value; } + const string &getName () const { return _name; } + const int getIndex () const { return _index; } + SGPropertyNode * getParent () { return _parent; } + const SGPropertyNode * getParent () const { return _parent; } + + // Children. + const int nChildren () const { return _children.size(); } + SGPropertyNode * getChild (int position); + const SGPropertyNode * getChild (int position) const; + SGPropertyNode * getChild (const string &name, int index = 0, + bool create = false); + const SGPropertyNode * getChild (const string &name, int index = 0) const; + + vector getChildren (const string &name); + vector getChildren (const string &name) const; + + // Path information. + string getPath (bool simplify = false) const; + + // Relative or absolute paths. + SGPropertyNode * getRootNode (); + const SGPropertyNode * getRootNode () const; + SGPropertyNode * getNode (const string &relative_path, bool create = false); + const SGPropertyNode * getNode (const string &relative_path) const; + + // Value-related stuff. + SGValue::Type getType () const; + + bool getBoolValue () const; + int getIntValue () const; + float getFloatValue () const; + double getDoubleValue () const; + string getStringValue () const; + + bool setBoolValue (bool value); + bool setIntValue (int value); + bool setFloatValue (float value); + bool setDoubleValue (double value); + bool setStringValue (string value); + bool setUnknownValue (string value); + + bool isTied () const; + + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); + + bool untie (); + + // Values from paths. + bool hasValue (const string &relative_path) const; + SGValue * getValue (const string &relative_path, bool create = false); + const SGValue * getValue (const string &relative_path) const; + + SGValue::Type getType (const string &relative_path) const; + + bool getBoolValue (const string &relative_path, + bool defaultValue = false) const; + int getIntValue (const string &relative_path, + int defaultValue = 0) const; + float getFloatValue (const string &relative_path, + float defaultValue = 0.0) const; + double getDoubleValue (const string &relative_path, + double defaultValue = 0.0L) const; + string getStringValue (const string &relative_path, + string defaultValue = "") const; + + bool setBoolValue (const string &relative_path, bool value); + bool setIntValue (const string &relative_path, int value); + bool setFloatValue (const string &relative_path, float value); + bool setDoubleValue (const string &relative_path, double value); + bool setStringValue (const string &relative_path, string value); + bool setUnknownValue (const string &relative_path, string value); + + bool isTied (const string &relative_path) const; + + bool tie (const string &relative_path, const SGRawValue &rawValue, + bool useDefault = true); + bool tie (const string &relative_path, const SGRawValue &rawValue, + bool useDefault = true); + bool tie (const string &relative_path, const SGRawValue &rawValue, + bool useDefault = true); + bool tie (const string &relative_path, const SGRawValue &rawValue, + bool useDefault = true); + bool tie (const string &relative_path, const SGRawValue &rawValue, + bool useDefault = true); + + bool untie (const string &relative_path); - // Accessor and setter for the real - // property list. - virtual SGPropertyList * getPropertyList () { return _props; } - virtual void setPropertyList (SGPropertyList * props) { - _props = props; - } +protected: - // Accessors for derived information. - virtual int size () const; - virtual const string &getName () const; - virtual SGPropertyNode &getParent () const; - virtual SGPropertyNode &getChild (int n) const; - virtual SGPropertyNode &getSubNode (const string &subpath) const; - - // Check for a value. - virtual bool hasValue (const string &subpath = "") const; - - // Get values directly. - virtual SGValue * getValue (const string &subpath = ""); - virtual bool getBoolValue (const string &subpath = "", - bool defaultValue = false) const; - virtual int getIntValue (const string &subpath = "", - int defaultValue = 0) const; - virtual float getFloatValue (const string &subpath = "", - float defaultValue = 0.0) const; - virtual double getDoubleValue (const string &subpath = "", - double defaultValue = 0.0L) const; - virtual const string & - getStringValue (const string &subpath = "", - const string &defaultValue = "") const; + SGPropertyNode (const string &name, int index, SGPropertyNode * parent); private: - string _path; - SGPropertyList * _props; - // for pointer persistence... - // NOT THREAD SAFE!!! - // (each thread must have its own node - // object) - mutable string _name; - mutable SGPropertyNode * _node; -}; + SGPropertyNode (const SGPropertyNode &node) {} - -//////////////////////////////////////////////////////////////////////// -// Input and output. -//////////////////////////////////////////////////////////////////////// + SGValue * _value; + string _name; + int _index; + SGPropertyNode * _parent; + vector _children; -extern bool readPropertyList (istream &input, SGPropertyList * props); -extern bool readPropertyList (const string &file, SGPropertyList * props); -extern bool writePropertyList (ostream &output, const SGPropertyList * props); -extern bool writePropertyList (const string &file, - const SGPropertyList * props); +}; //////////////////////////////////////////////////////////////////////// -// Global property manager. +// I/O functions. //////////////////////////////////////////////////////////////////////// -extern SGPropertyList current_properties; +bool readProperties (istream &input, SGPropertyNode * start_node); +bool readProperties (const string &file, SGPropertyNode * start_node); +bool writeProperties (ostream &output, const SGPropertyNode * start_node); +bool writeProperties (const string &file, const SGPropertyNode * start_node); #endif // __PROPS_HXX diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx index d7671bd1..2a0196b7 100644 --- a/simgear/misc/props_io.cxx +++ b/simgear/misc/props_io.cxx @@ -3,7 +3,6 @@ # include #endif -#include // strcmp() #include // atof() atoi() #include @@ -16,200 +15,159 @@ #include #include -FG_USING_STD(ofstream); -FG_USING_STD(ifstream); -FG_USING_STD(string); -FG_USING_STD(vector); +using std::istream; +using std::ifstream; +using std::ostream; +using std::ofstream; +using std::string; +using std::vector; //////////////////////////////////////////////////////////////////////// -// Visitor class for building the property list. +// Property list visitor, for XML parsing. //////////////////////////////////////////////////////////////////////// -class PropVisitor : public XMLVisitor +class PropsVisitor : public XMLVisitor { public: - PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {} - void startDocument (); + + PropsVisitor (SGPropertyNode * root) : _ok(true), _root(root), _level(0) {} + + 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 col); - void error (const char * message, int line, int col); + void warning (const char * message, int line, int column); + void error (const char * message, int line, int column); bool isOK () const { return _ok; } private: - void pushState (const char * name) { - _states.push_back(_state); + struct State + { + State () : node(0), type("") {} + State (SGPropertyNode * _node, const char * _type) + : node(_node), type(_type) {} + SGPropertyNode * node; + string type; + }; + + State &state () { return _state_stack[_state_stack.size() - 1]; } + + void push_state (SGPropertyNode * node, const char * type) { + if (type == 0) + _state_stack.push_back(State(node, "unknown")); + else + _state_stack.push_back(State(node, type)); _level++; - _state.name = name; - _state.type = SGValue::UNKNOWN; - _state.data = ""; - _state.hasChildren = false; - _state.hasData = false; + _data = ""; } - void popState () { - _state = _states.back(); - _states.pop_back(); + void pop_state () { + _state_stack.pop_back(); _level--; } - struct State - { - State () : hasChildren(false), hasData(false) {} - string name; - SGValue::Type type; - string data; - bool hasChildren; - bool hasData; - }; - - SGPropertyList * _props; - State _state; - vector _states; - int _level; bool _ok; + string _data; + SGPropertyNode * _root; + int _level; + vector _state_stack; + }; void -PropVisitor::startDocument () +PropsVisitor::startXML () { _level = 0; - _ok = true; + _state_stack.resize(0); } +void +PropsVisitor::endXML () +{ + _level = 0; + _state_stack.resize(0); +} void -PropVisitor::startElement (const char * name, const XMLAttributes &atts) +PropsVisitor::startElement (const char * name, const XMLAttributes &atts) { - if (!_ok) - return; - - if (_level == 0 && strcmp(name, "PropertyList")) { - _ok = false; - FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \"" - << name << "\" instead of \"PropertyList\""); - return; + if (_level == 0) { + push_state(_root, ""); } - // Mixed content? - _state.hasChildren = true; - if (_state.hasData) { - FG_LOG(FG_INPUT, FG_ALERT, - "XML element has mixed elements and data in element " - << _state.name); - _ok = false; - return; + else { + const char * att_n = atts.getValue("n"); + int index = 0; + if (att_n != 0) + index = atoi(att_n); + push_state(state().node->getChild(name, index, true), + atts.getValue("type")); } - - // Start a new state. - pushState(name); - - // See if there's a type specified. - const char * type = atts.getValue("type"); - if (type == 0 || !strcmp("unknown", type)) - _state.type = SGValue::UNKNOWN; - else if (!strcmp("bool", type)) - _state.type = SGValue::BOOL; - else if (!strcmp("int", type)) - _state.type = SGValue::INT; - else if (!strcmp("float", type)) - _state.type = SGValue::FLOAT; - else if (!strcmp("double", type)) - _state.type = SGValue::DOUBLE; - else if (!strcmp("string", type)) - _state.type = SGValue::STRING; - else - FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type - << ", using UNKNOWN"); } void -PropVisitor::endElement (const char * name) +PropsVisitor::endElement (const char * name) { - if (!_ok) - return; - - // See if there's a property to add. - if (_state.hasData) { - bool status = false; - - // Figure out the path name. - string path = ""; - for (int i = 2; i < _level; i++) { - path += '/'; - path += _states[i].name; - } - path += '/'; - path += _state.name; - - // Set the value - switch (_state.type) { - case SGValue::BOOL: - if (_state.data == "true" || _state.data == "TRUE") { - status = _props->setBoolValue(path, true); - } else if (atof(_state.data.c_str()) != 0.0) { - status = _props->setBoolValue(path, true); - } else { - status =_props->setBoolValue(path, false); - } - break; - case SGValue::INT : - status = _props->setIntValue(path, atoi(_state.data.c_str())); - break; - case SGValue::FLOAT: - status = _props->setFloatValue(path, atof(_state.data.c_str())); - break; - case SGValue::DOUBLE: - status = _props->setDoubleValue(path, atof(_state.data.c_str())); - break; - case SGValue::STRING: - status = _props->setStringValue(path, _state.data); - break; - default: - status = _props->setUnknownValue(path, _state.data); - break; + State &st = state(); + bool ret; + + // If there are no children, then + // it is a leaf value. + if (st.node->nChildren() == 0) { + 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 == "float") { + ret = st.node->setFloatValue(atof(_data.c_str())); + } else if (st.type == "double") { + ret = st.node->setDoubleValue(atof(_data.c_str())); + } else if (st.type == "string") { + ret = st.node->setStringValue(_data); + } else if (st.type == "unknown") { + ret = st.node->setUnknownValue(_data); + } else { + FG_LOG(FG_INPUT, FG_ALERT, "Unknown data type " << st.type + << " assuming 'unknown'"); + ret = st.node->setUnknownValue(_data); } - if (!status) - FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property " - << path << " to " << _state.data); } - // Pop the stack. - popState(); + if (!ret) + FG_LOG(FG_INPUT, FG_ALERT, "readProperties: Failed to set " + << st.node->getPath() << " to value \"" + << _data << " with type " << st.type); + + pop_state(); } void -PropVisitor::data (const char * s, int length) +PropsVisitor::data (const char * s, int length) { - if (!_ok) - return; - - // Check if there is any non-whitespace - if (!_state.hasData) - for (int i = 0; i < length; i++) - if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r') - _state.hasData = true; - - _state.data += string(s, length); // FIXME: inefficient + if (state().node->nChildren() == 0) + _data.append(string(s, length)); } void -PropVisitor::warning (const char * message, int line, int col) +PropsVisitor::warning (const char * message, int line, int column) { - FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: " - << message << " (" << line << ',' << col << ')'); + FG_LOG(FG_INPUT, FG_ALERT, "readProperties: warning: " + << message << " at line " << line << ", column " << column); } void -PropVisitor::error (const char * message, int line, int col) +PropsVisitor::error (const char * message, int line, int column) { - FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: " - << message << " (" << line << ',' << col << ')'); + FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: " + << message << " at line " << line << ", column " << column); _ok = false; } @@ -220,18 +178,18 @@ PropVisitor::error (const char * message, int line, int col) //////////////////////////////////////////////////////////////////////// bool -readPropertyList (istream &input, SGPropertyList * props) +readProperties (istream &input, SGPropertyNode * start_node) { - PropVisitor visitor(props); + PropsVisitor visitor(start_node); return readXML(input, visitor) && visitor.isOK(); } bool -readPropertyList (const string &file, SGPropertyList * props) +readProperties (const string &file, SGPropertyNode * start_node) { ifstream input(file.c_str()); if (input.good()) { - return readPropertyList(input, props); + return readProperties(input, start_node); } else { FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file " << file); @@ -267,8 +225,6 @@ getTypeName (SGValue::Type type) case SGValue::STRING: return "string"; } - - return "unknown"; // avoid a compiler warning } @@ -278,7 +234,7 @@ getTypeName (SGValue::Type type) static void writeData (ostream &output, const string &data) { - for (int i = 0; i < (int)data.size(); i++) { + for (int i = 0; i < data.size(); i++) { switch (data[i]) { case '&': output << "&"; @@ -306,48 +262,54 @@ doIndent (ostream &output, int indent) static bool -writeNode (ostream &output, SGPropertyNode node, int indent) +writeNode (ostream &output, const SGPropertyNode * node, int indent) { - const string &name = node.getName(); - int size = node.size(); + const string &name = node->getName(); + int index = node->getIndex(); + int nChildren = node->nChildren(); - // Write out the literal value, if any. - SGValue * value = node.getValue(); - if (value != 0) { - SGValue::Type type = value->getType(); + // If there is a literal value, + // write it first. + if (node->hasValue()) { doIndent(output, indent); - output << '<' << name; - if (type != SGValue::UNKNOWN) - output << " type=\"" << getTypeName(type) << '"'; - output << '>'; - writeData(output, value->getStringValue()); - output << "' << endl; + output << '<' << name << " n=\"" << index + << "\" type=\"" << getTypeName(node->getType()) << "\">"; + writeData(output, node->getStringValue()); + output << "' << endl;; } - // Write out the children, if any. - if (size > 0) { + // If there are children, write them + // next. + if (nChildren > 0) { doIndent(output, indent); - output << '<' << name << '>' << endl;; - for (int i = 0; i < size; i++) { - writeNode(output, node.getChild(i), indent + INDENT_STEP); - } + output << '<' << name << " n=\"" << index << "\">" << endl;; + for (int i = 0; i < nChildren; i++) + writeNode(output, node->getChild(i), indent + INDENT_STEP); doIndent(output, indent); output << "' << endl; } + // If there were no children and no + // value, at least note the presence + // of the node. + if (nChildren == 0 && !node->hasValue()) { + doIndent(output, indent); + output << '<' << name << " n=\"" << index << "\"/>" << endl; + } + return true; } bool -writePropertyList (ostream &output, const SGPropertyList * props) +writeProperties (ostream &output, const SGPropertyNode * start_node) { - SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME + int nChildren = start_node->nChildren(); output << "" << endl << endl; output << "" << endl; - for (int i = 0; i < root.size(); i++) { - writeNode(output, root.getChild(i), INDENT_STEP); + for (int i = 0; i < nChildren; i++) { + writeNode(output, start_node->getChild(i), INDENT_STEP); } output << "" << endl; @@ -356,13 +318,13 @@ writePropertyList (ostream &output, const SGPropertyList * props) } bool -writePropertyList (const string &file, const SGPropertyList * props) +writeProperties (const string &file, const SGPropertyNode * start_node) { ofstream output(file.c_str()); if (output.good()) { - return writePropertyList(output, props); + return writeProperties(output, start_node); } else { - FG_LOG(FG_INPUT, FG_ALERT, "Cannot write property list to file " + FG_LOG(FG_INPUT, FG_ALERT, "Cannot write properties to file " << file); return false; } diff --git a/simgear/misc/props_test.cxx b/simgear/misc/props_test.cxx new file mode 100644 index 00000000..2f033ea6 --- /dev/null +++ b/simgear/misc/props_test.cxx @@ -0,0 +1,338 @@ + +//////////////////////////////////////////////////////////////////////// +// Test harness. +//////////////////////////////////////////////////////////////////////// + +#include +#include "props.hxx" + +using std::cout; +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 SGValue * value) +{ + cout << "Bool: " << value->getBoolValue() << endl; + cout << "Int: " << value->getIntValue() << endl; + cout << "Float: " << value->getFloatValue() << endl; + cout << "Double: " << value->getDoubleValue() << endl; + cout << "String: " << value->getStringValue() << endl; +} + + + +//////////////////////////////////////////////////////////////////////// +// Test individual values. +//////////////////////////////////////////////////////////////////////// + +static void +test_value () +{ + SGValue * value; + + cout << endl << "Value" << endl << endl; + + // + // Test coercion for getters. + // + + cout << "Testing coercion from bool (expect true)" << endl; + value = new SGValue; + value->setBoolValue(true); + show_values(value); + delete value; + cout << endl; + + cout << "Testing coercion from int (expect 128)" << endl; + value = new SGValue; + value->setIntValue(128); + show_values(value); + delete value; + cout << endl; + + cout << "Testing coercion from float (expect 1.0/3.0)" << endl; + value = new SGValue; + value->setFloatValue(1.0/3.0); + show_values(value); + delete value; + cout << endl; + + cout << "Testing coercion from double (expect 1.0/3.0)" << endl; + value = new SGValue; + value->setDoubleValue(1.0/3.0); + show_values(value); + delete value; + cout << endl; + + cout << "Testing coercion from string (expect 10e4)" << endl; + value = new SGValue; + value->setStringValue("10e4"); + show_values(value); + delete value; + cout << endl; + + cout << "Testing coercion from unknown (expect -10e-4)" << endl; + value = new SGValue; + value->setUnknownValue("-10e-4"); + show_values(value); + delete value; + cout << endl; + + // + // Test coercion for setters. + // + + value = new SGValue; + + cout << "Testing coercion to bool from bool (expect false)" << endl; + value->setBoolValue(false); + show_values(value); + cout << endl; + + cout << "Testing coercion to bool from int (expect 1)" << endl; + value->setIntValue(1); + show_values(value); + cout << endl; + + cout << "Testing coercion to bool from float (expect 1.1)" << endl; + value->setFloatValue(1.1); + show_values(value); + cout << endl; + + cout << "Testing coercion to bool from double (expect 1.1)" << endl; + value->setDoubleValue(1.1); + show_values(value); + cout << endl; + + cout << "Testing coercion to bool from string (expect 1e10)" << endl; + value->setStringValue("1e10"); + show_values(value); + cout << endl; + + cout << "Testing coercion to bool from unknown (expect 1e10)" << endl; + value->setUnknownValue("1e10"); + show_values(value); + cout << endl; + + // Test tying to a pointer. + + static int myValue = 10; + + cout << "Testing tying to a pointer (expect 10)" << endl; + if (!value->tie(SGRawValuePointer(&myValue))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Changing base variable (expect -5)" << endl; + myValue = -5; + show_values(value); + if (!value->untie()) + cout << "*** FAIELD TO UNTIE VALUE!!!" << endl; + cout << endl; + + + // Test tying to static functions. + + cout << "Create a new int value (expect 10)" << endl; + value->setIntValue(10); + show_values(value); + cout << endl; + + cout << "Testing tying to static getter (expect 100)" << endl; + if (!value->tie(SGRawValueFunctions(get100))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Try changing value with no setter (expect 100)" << endl; + if (value->setIntValue(200)) + cout << "*** setIntValue did not return false!!!" << endl; + show_values(value); + cout << endl; + + cout << "Untie value (expect 100)" << endl; + if (!value->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Try changing value (expect 200)" << endl; + if (!value->setIntValue(200)) + cout << "*** setIntValue RETURNED FALSE!!!" << endl; + show_values(value); + cout << endl; + + // Test tying to indexed static functions. + + cout << "Create a new int value (expect 10)" << endl; + value->setIntValue(10); + show_values(value); + cout << endl; + + cout << "Testing tying to indexed static getter (0.3333...)" << endl; + if (!value->tie(SGRawValueFunctionsIndexed(3, getNum))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Untie value (expect 0.3333...)" << endl; + if (!value->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + show_values(value); + 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 (!value->tie(tiedstuff, false)) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Try untying from object (expect 199)" << endl; + if (!value->untie()) + cout << "*** FAILED TO UNTIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Try tying to an indexed method (expect 199)" << endl; + if (!value->tie(SGRawValueMethodsIndexed + (stuff, 2, &Stuff::getStuff, &Stuff::setStuff))) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + value->untie(); + + cout << "Change value (expect 50)" << endl; + if (!value->setIntValue(50)) + cout << "*** FAILED TO SET VALUE!!!" << endl; + show_values(value); + cout << endl; + + cout << "Try tying to an object with defaults (expect 50)" << endl; + if (!value->tie(tiedstuff, true)) + cout << "*** FAILED TO TIE VALUE!!!" << endl; + show_values(value); + cout << endl; + + delete value; +} + + + +//////////////////////////////////////////////////////////////////////// +// Check property nodes. +//////////////////////////////////////////////////////////////////////// + +static void +dump_node (const SGPropertyNode * node) +{ + writeProperties(cout, node); +} + +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 < 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; +} + + +main (int ac, char ** av) +{ + test_value(); + test_property_nodes(); + + for (int i = 1; i < ac; i++) { + cout << "Reading " << av[i] << endl; + SGPropertyNode root; + if (!readProperties(av[i], &root)) + cerr << "Failed to read properties from " << av[i] << endl; + else + writeProperties(cout, &root); + cout << endl; + } +} -- 2.39.5