X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Fprops.cxx;h=fdabb24ca44bd2f5afdd868f93908e6a0e2fcce9;hb=2c4a0dd9985121cbb98c79ea1832360f163f9d4e;hp=cbc3306865648512c8dd1706b419232dbf9bc3ba;hpb=7d251444e6e1eca8fc9ed456215acbb4ef207456;p=simgear.git diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx index cbc33068..fdabb24c 100644 --- a/simgear/misc/props.cxx +++ b/simgear/misc/props.cxx @@ -1,20 +1,14 @@ -// props.cxx -- implementation of FGFS global properties. +// props.cxx -- implementation of SimGear Property Manager. // -// Copyright (C) 2000 David Megginson - david@megginson.com +// Written by David Megginson - david@megginson.com // -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. +// This 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. See the GNU -// General Public License for more details. +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// See props.html for documentation [replace with URL when available]. // // $Id$ @@ -32,21 +26,21 @@ using std::string; -FGPropertyList current_properties; +SGPropertyList current_properties; static string empty_string; //////////////////////////////////////////////////////////////////////// -// Implementation of FGValue. +// Implementation of SGValue. //////////////////////////////////////////////////////////////////////// /** * Construct a new value. */ -FGValue::FGValue () +SGValue::SGValue () : _type(UNKNOWN), _tied(false) { } @@ -55,17 +49,16 @@ FGValue::FGValue () /** * Destroy a value. */ -FGValue::~FGValue () +SGValue::~SGValue () { - if (!_tied && _type == STRING) { - delete _value.string_val; - _value.string_val = 0; - } } +/** + * Return a raw boolean value (no type coercion). + */ bool -FGValue::getRawBool () const +SGValue::getRawBool () const { if (_tied) { if (_value.bool_func.getter != 0) @@ -78,8 +71,11 @@ FGValue::getRawBool () const } +/** + * Return a raw integer value (no type coercion). + */ int -FGValue::getRawInt () const +SGValue::getRawInt () const { if (_tied) { if (_value.int_func.getter != 0) @@ -92,8 +88,11 @@ FGValue::getRawInt () const } +/** + * Return a raw floating-point value (no type coercion). + */ float -FGValue::getRawFloat () const +SGValue::getRawFloat () const { if (_tied) { if (_value.float_func.getter != 0) @@ -106,8 +105,11 @@ FGValue::getRawFloat () const } +/** + * Return a raw double-precision floating-point value (no type coercion). + */ double -FGValue::getRawDouble () const +SGValue::getRawDouble () const { if (_tied) { if (_value.double_func.getter != 0) @@ -120,22 +122,26 @@ FGValue::getRawDouble () const } +/** + * Return a raw string value (no type coercion). + */ const string & -FGValue::getRawString () const +SGValue::getRawString () const { - if (_tied) { - if (_value.string_func.getter != 0) - return (*(_value.string_func.getter))(); - else - return empty_string; - } else { - return *_value.string_val; - } + if (_tied && _value.string_func.getter != 0) + return (*(_value.string_func.getter))(); + else + return string_val; } +/** + * Set a raw boolean value (no type coercion). + * + * Return false if the value could not be set, true otherwise. + */ bool -FGValue::setRawBool (bool value) +SGValue::setRawBool (bool value) { if (_tied) { if (_value.bool_func.setter != 0) { @@ -151,8 +157,13 @@ FGValue::setRawBool (bool value) } +/** + * Set a raw integer value (no type coercion). + * + * Return false if the value could not be set, true otherwise. + */ bool -FGValue::setRawInt (int value) +SGValue::setRawInt (int value) { if (_tied) { if (_value.int_func.setter != 0) { @@ -168,8 +179,13 @@ FGValue::setRawInt (int value) } +/** + * Set a raw floating-point value (no type coercion). + * + * Return false if the value could not be set, true otherwise. + */ bool -FGValue::setRawFloat (float value) +SGValue::setRawFloat (float value) { if (_tied) { if (_value.float_func.setter != 0) { @@ -185,8 +201,13 @@ FGValue::setRawFloat (float value) } +/** + * Set a raw double-precision floating-point value (no type coercion). + * + * Return false if the value could not be set, true otherwise. + */ bool -FGValue::setRawDouble (double value) +SGValue::setRawDouble (double value) { if (_tied) { if (_value.double_func.setter != 0) { @@ -202,8 +223,13 @@ FGValue::setRawDouble (double value) } +/** + * Set a raw string value (no type coercion). + * + * Return false if the value could not be set, true otherwise. + */ bool -FGValue::setRawString (const string &value) +SGValue::setRawString (const string &value) { if (_tied) { if (_value.string_func.setter != 0) { @@ -213,23 +239,21 @@ FGValue::setRawString (const string &value) return false; } } else { - if (_value.string_val == 0) - _value.string_val = new string; - *(_value.string_val) = value; + string_val = value; return true; } } /** - * Attempt to get the boolean value of a property. + * Get the boolean value of a property. + * + * If the native type is not boolean, attempt to coerce it. */ bool -FGValue::getBoolValue () const +SGValue::getBoolValue () const { switch (_type) { - case UNKNOWN: - return false; case BOOL: return getRawBool(); case INT: @@ -238,23 +262,23 @@ FGValue::getBoolValue () const return (getRawFloat() == 0.0 ? false : true); case DOUBLE: return (getRawDouble() == 0.0 ? false : true); + case UNKNOWN: case STRING: - return (getRawString() == "false" ? false : true); + return ((getRawString() == "true" || getIntValue() != 0) ? true : false); } - return false; } /** - * Attempt to get the integer value of a property. + * Get the integer value of a property. + * + * If the native type is not integer, attempt to coerce it. */ int -FGValue::getIntValue () const +SGValue::getIntValue () const { switch (_type) { - case UNKNOWN: - return 0; case BOOL: return getRawBool(); case INT: @@ -263,23 +287,23 @@ FGValue::getIntValue () const return (int)(getRawFloat()); case DOUBLE: return (int)(getRawDouble()); + case UNKNOWN: case STRING: return atoi(getRawString().c_str()); } - - return 0; + return false; } /** - * Attempt to get the float value of a property. + * Get the floating-point value of a property. + * + * If the native type is not float, attempt to coerce it. */ float -FGValue::getFloatValue () const +SGValue::getFloatValue () const { switch (_type) { - case UNKNOWN: - return 0.0; case BOOL: return (float)(getRawBool()); case INT: @@ -288,23 +312,23 @@ FGValue::getFloatValue () const return getRawFloat(); case DOUBLE: return (float)(getRawDouble()); + case UNKNOWN: case STRING: return (float)atof(getRawString().c_str()); } - - return 0.0; + return false; } /** - * Attempt to get the double value of a property. + * Get the double-precision floating-point value of a property. + * + * If the native type is not double, attempt to coerce it. */ double -FGValue::getDoubleValue () const +SGValue::getDoubleValue () const { switch (_type) { - case UNKNOWN: - return 0.0; case BOOL: return (double)(getRawBool()); case INT: @@ -313,103 +337,246 @@ FGValue::getDoubleValue () const return (double)(getRawFloat()); case DOUBLE: return getRawDouble(); + case UNKNOWN: case STRING: return atof(getRawString().c_str()); } - - return 0.0; + return false; } /** - * Attempt to get the string value of a property. + * Get the string value of a property. + * + * If the native type is not string, attempt to coerce it. */ const string & -FGValue::getStringValue () const +SGValue::getStringValue () const { + char buf[512]; switch (_type) { - case UNKNOWN: case BOOL: + if (getRawBool()) + string_val = "true"; + else + string_val = "false"; + return string_val; case INT: + sprintf(buf, "%d", getRawInt()); + string_val = buf; + return string_val; case FLOAT: + sprintf(buf, "%f", getRawFloat()); + string_val = buf; + return string_val; case DOUBLE: - return empty_string; + sprintf(buf, "%f", getRawDouble()); + string_val = buf; + return string_val; + case UNKNOWN: case STRING: return getRawString(); } - return empty_string; } +/** + * Set the boolean value and change the type if unknown. + * + * Returns true on success. + */ bool -FGValue::setBoolValue (bool value) +SGValue::setBoolValue (bool value) { - if (_type == UNKNOWN || _type == BOOL) { - _type = BOOL; + if (_type == UNKNOWN) + _type = INT; + switch (_type) { + case BOOL: return setRawBool(value); - } else { - return false; + case INT: + return setRawInt((int)value); + case FLOAT: + return setRawFloat((float)value); + case DOUBLE: + return setRawDouble((double)value); + case STRING: + if (value) + return setRawString("true"); + else + return setRawString("false"); } + return false; } +/** + * Set the integer value and change the type if unknown. + * + * Returns true on success. + */ bool -FGValue::setIntValue (int value) +SGValue::setIntValue (int value) { - if (_type == UNKNOWN || _type == INT) { + if (_type == UNKNOWN) _type = INT; + switch (_type) { + case BOOL: + if (value == 0) + return setRawBool(false); + else + return setRawBool(true); + case INT: return setRawInt(value); - } else { - return false; + case FLOAT: + return setRawFloat((float)value); + case DOUBLE: + return setRawDouble((double)value); + case STRING: + char buf[128]; + sprintf(buf, "%d", value); + return setRawString(buf); } + return false; } +/** + * Set the floating-point value and change the type if unknown. + * + * Returns true on success. + */ bool -FGValue::setFloatValue (float value) +SGValue::setFloatValue (float value) { - if (_type == UNKNOWN || _type == FLOAT) { + if (_type == UNKNOWN) _type = FLOAT; + switch (_type) { + case BOOL: + if (value == 0.0) + return setRawBool(false); + else + return setRawBool(true); + case INT: + return setRawInt((int)value); + case FLOAT: return setRawFloat(value); - } else { - return false; + case DOUBLE: + return setRawDouble((double)value); + case STRING: + char buf[128]; + sprintf(buf, "%f", value); + return setRawString(buf); } + return false; } +/** + * Set the double-precision value and change the type if unknown. + * + * Returns true on success. + */ bool -FGValue::setDoubleValue (double value) +SGValue::setDoubleValue (double value) { - if (_type == UNKNOWN || _type == DOUBLE) { + if (_type == UNKNOWN) _type = DOUBLE; + switch (_type) { + case BOOL: + if (value == 0.0L) + return setRawBool(false); + else + return setRawBool(true); + case INT: + return setRawInt((int)value); + case FLOAT: + return setRawFloat((float)value); + case DOUBLE: return setRawDouble(value); - } else { - return false; + case STRING: + char buf[128]; + sprintf(buf, "%lf", value); + return setRawString(buf); } + return false; } +/** + * Set the string value and change the type if unknown. + * + * Returns true on success. + */ bool -FGValue::setStringValue (const string &value) +SGValue::setStringValue (const string &value) { - if (_type == UNKNOWN || _type == STRING) { + if (_type == UNKNOWN) _type = STRING; + + switch (_type) { + case BOOL: + if (value == "true" || atoi(value.c_str()) != 0) + return setRawBool(true); + else + return setRawBool(false); + case INT: + return setRawInt(atoi(value.c_str())); + case FLOAT: + return setRawFloat(atof(value.c_str())); + case DOUBLE: + return setRawDouble(atof(value.c_str())); + case STRING: + return setRawString(value); + } + return false; +} + + +/** + * Set a string value and don't modify the type. + * + * Returns true on success. + */ +bool +SGValue::setUnknownValue (const string &value) +{ + switch (_type) { + case BOOL: + if (value == "true" || atoi(value.c_str()) != 0) + return setRawBool(true); + else + return setRawBool(false); + case INT: + return setRawInt(atoi(value.c_str())); + case FLOAT: + return setRawFloat(atof(value.c_str())); + case DOUBLE: + return setRawDouble(atof(value.c_str())); + case STRING: + case UNKNOWN: return setRawString(value); - } else { - return false; } + 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). + */ bool -FGValue::tieBool (bool_getter getter, bool_setter setter = 0, - bool useDefault = true) +SGValue::tieBool (bool_getter getter, bool_setter setter, + bool useDefault) { if (_tied) { return false; } else { - if (useDefault && setter && _type != UNKNOWN) + if (useDefault && setter) (*setter)(getBoolValue()); _tied = true; _type = BOOL; @@ -420,14 +587,22 @@ FGValue::tieBool (bool_getter getter, bool_setter setter = 0, } +/** + * 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). + */ bool -FGValue::tieInt (int_getter getter, int_setter setter = 0, - bool useDefault = true) +SGValue::tieInt (int_getter getter, int_setter setter, + bool useDefault) { if (_tied) { return false; } else { - if (useDefault && setter && _type != UNKNOWN) + if (useDefault && setter) (*setter)(getIntValue()); _tied = true; _type = INT; @@ -438,14 +613,22 @@ FGValue::tieInt (int_getter getter, int_setter setter = 0, } +/** + * 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). + */ bool -FGValue::tieFloat (float_getter getter, float_setter setter = 0, - bool useDefault = true) +SGValue::tieFloat (float_getter getter, float_setter setter, + bool useDefault) { if (_tied) { return false; } else { - if (useDefault && setter && _type != UNKNOWN) + if (useDefault && setter) (*setter)(getFloatValue()); _tied = true; _type = FLOAT; @@ -456,14 +639,22 @@ FGValue::tieFloat (float_getter getter, float_setter setter = 0, } +/** + * 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). + */ bool -FGValue::tieDouble (double_getter getter, double_setter setter = 0, - bool useDefault = true) +SGValue::tieDouble (double_getter getter, double_setter setter, + bool useDefault) { if (_tied) { return false; } else { - if (useDefault && setter && _type != UNKNOWN) + if (useDefault && setter) (*setter)(getDoubleValue()); _tied = true; _type = DOUBLE; @@ -474,17 +665,23 @@ FGValue::tieDouble (double_getter getter, double_setter setter = 0, } +/** + * 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). + */ bool -FGValue::tieString (string_getter getter, string_setter setter = 0, - bool useDefault = true) +SGValue::tieString (string_getter getter, string_setter setter, + bool useDefault) { if (_tied) { return false; } else { - if (useDefault && setter && _type != UNKNOWN) + if (useDefault && setter) (*setter)(getStringValue()); - if (_type == STRING) - delete _value.string_val; _tied = true; _type = STRING; _value.string_func.getter = getter; @@ -494,92 +691,102 @@ FGValue::tieString (string_getter getter, string_setter setter = 0, } +/** + * 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). + */ bool -FGValue::untieBool () +SGValue::untie () { - if (_tied && _type == BOOL) { + if (!_tied) + return false; + + switch (_type) { + case BOOL: { bool value = getRawBool(); - _value.bool_val = value; _tied = false; - return true; - } else { - return false; + setRawBool(value); + break; } -} - - -bool -FGValue::untieInt () -{ - if (_tied && _type == INT) { + case INT: { int value = getRawInt(); - _value.int_val = value; _tied = false; - return true; - } else { - return false; + setRawInt(value); + break; } -} - - -bool -FGValue::untieFloat () -{ - if (_tied && _type == FLOAT) { + case FLOAT: { float value = getRawFloat(); - _value.float_val = value; _tied = false; - return true; - } else { - return false; + setRawFloat(value); + break; } -} - - -bool -FGValue::untieDouble () -{ - if (_tied && _type == DOUBLE) { + case DOUBLE: { double value = getRawDouble(); - _value.double_val = value; _tied = false; - return true; - } else { - return false; + setRawDouble(value); + break; } -} - - -bool -FGValue::untieString () -{ - if (_tied && _type == STRING) { - const string &value = getRawString(); - _value.string_val = new string(value); + case STRING: { + string value = getRawString(); _tied = false; - return true; - } else { - return false; + setRawString(value); + break; + } } + + return true; } //////////////////////////////////////////////////////////////////////// -// Implementation of FGPropertyList. +// Implementation of SGPropertyList. //////////////////////////////////////////////////////////////////////// -FGPropertyList::FGPropertyList () + +/** + * Constructor. + */ +SGPropertyList::SGPropertyList () { } -FGPropertyList::~FGPropertyList () + +/** + * Destructor. + */ +SGPropertyList::~SGPropertyList () { } -FGValue * -FGPropertyList::getValue (const string &name, bool create = false) +/** + * Return true if a value is present. + */ +bool +SGPropertyList::hasValue (const string &name) const +{ + const_iterator el = _props.find(name); + if (el == _props.end()) + return false; + else + return true; +} + + +/** + * 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()) { @@ -587,217 +794,630 @@ FGPropertyList::getValue (const string &name, bool create = false) return 0; else { FG_LOG(FG_GENERAL, FG_INFO, "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; + } } } return &(_props[name]); } -const FGValue * -FGPropertyList::getValue (const string &name) const +/** + * Look up a const value (never created). + */ +const SGValue * +SGPropertyList::getValue (const string &name) const { value_map::const_iterator el = _props.find(name); - return &(el->second); + if (el == _props.end()) + return 0; + else + return &(el->second); } +/** + * 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. + */ bool -FGPropertyList::getBoolValue (const string &name) const +SGPropertyList::getBoolValue (const string &name, bool defaultValue) const { - const FGValue * val = getValue(name); + const SGValue * val = getValue(name); if (val == 0) - return false; + return defaultValue; else return val->getBoolValue(); } +/** + * 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. + */ int -FGPropertyList::getIntValue (const string &name) const +SGPropertyList::getIntValue (const string &name, int defaultValue) const { - const FGValue * val = getValue(name); + const SGValue * val = getValue(name); if (val == 0) - return 0; + return defaultValue; else return val->getIntValue(); } +/** + * 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 -FGPropertyList::getFloatValue (const string &name) const +SGPropertyList::getFloatValue (const string &name, float defaultValue) const { - const FGValue * val = getValue(name); + const SGValue * val = getValue(name); if (val == 0) - return 0.0; + return defaultValue; else return val->getFloatValue(); } +/** + * 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 -FGPropertyList::getDoubleValue (const string &name) const +SGPropertyList::getDoubleValue (const string &name, double defaultValue) const { - const FGValue * val = getValue(name); + const SGValue * val = getValue(name); if (val == 0) - return 0.0; + return defaultValue; else return val->getDoubleValue(); } +/** + * 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 & -FGPropertyList::getStringValue (const string &name) const +SGPropertyList::getStringValue (const string &name, + const string &defaultValue) const { - const FGValue * val = getValue(name); + const SGValue * val = getValue(name); if (val == 0) - return empty_string; + return defaultValue; else return val->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 -FGPropertyList::setBoolValue (const string &name, bool value) +SGPropertyList::setBoolValue (const string &name, bool value) { return getValue(name, true)->setBoolValue(value); } +/** + * 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 -FGPropertyList::setIntValue (const string &name, int value) +SGPropertyList::setIntValue (const string &name, int value) { return getValue(name, true)->setIntValue(value); } +/** + * 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 -FGPropertyList::setFloatValue (const string &name, float value) +SGPropertyList::setFloatValue (const string &name, float value) { return getValue(name, true)->setFloatValue(value); } +/** + * 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 -FGPropertyList::setDoubleValue (const string &name, double value) +SGPropertyList::setDoubleValue (const string &name, double value) { return getValue(name, true)->setDoubleValue(value); } +/** + * 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 -FGPropertyList::setStringValue (const string &name, const string &value) +SGPropertyList::setStringValue (const string &name, const string &value) { return getValue(name, true)->setStringValue(value); } +/** + * 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) +{ + return getValue(name, true)->setUnknownValue(value); +} + + +/** + * Tie a boolean value to external functions. + * + * Invokes SGValue::tieBool + */ bool -FGPropertyList::tieBool (const string &name, +SGPropertyList::tieBool (const string &name, bool_getter getter, bool_setter setter, - bool useDefault = true) + bool useDefault) { FG_LOG(FG_GENERAL, FG_INFO, "Tying bool property '" << name << '\''); + useDefault = useDefault && hasValue(name); return getValue(name, true)->tieBool(getter, setter, useDefault); } +/** + * Tie an integer value to external functions. + * + * Invokes SGValue::tieInt + */ bool -FGPropertyList::tieInt (const string &name, +SGPropertyList::tieInt (const string &name, int_getter getter, int_setter setter, - bool useDefault = true) + bool useDefault) { FG_LOG(FG_GENERAL, FG_INFO, "Tying int property '" << name << '\''); + useDefault = useDefault && hasValue(name); return getValue(name, true)->tieInt(getter, setter, useDefault); } +/** + * Tie a float value to external functions. + * + * Invokes SGValue::tieFloat + */ bool -FGPropertyList::tieFloat (const string &name, +SGPropertyList::tieFloat (const string &name, float_getter getter, float_setter setter, - bool useDefault = true) + bool useDefault) { FG_LOG(FG_GENERAL, FG_INFO, "Tying float property '" << name << '\''); + useDefault = useDefault && hasValue(name); return getValue(name, true)->tieFloat(getter, setter, useDefault); } +/** + * Tie a double value to external functions. + * + * Invokes SGValue::tieDouble + */ bool -FGPropertyList::tieDouble (const string &name, +SGPropertyList::tieDouble (const string &name, double_getter getter, double_setter setter, - bool useDefault = true) + bool useDefault) { FG_LOG(FG_GENERAL, FG_INFO, "Tying double property '" << name << '\''); + useDefault = useDefault && hasValue(name); return getValue(name, true)->tieDouble(getter, setter, useDefault); } +/** + * Tie a string value to external functions. + * + * Invokes SGValue::tieString + */ bool -FGPropertyList::tieString (const string &name, +SGPropertyList::tieString (const string &name, string_getter getter, string_setter setter, - bool useDefault = true) + bool useDefault) { FG_LOG(FG_GENERAL, FG_INFO, "Tying string property '" << name << '\''); + useDefault = useDefault && hasValue(name); return getValue(name, true)->tieString(getter, setter, useDefault); } +/** + * Untie a value from external functions. + * + * Invokes SGValue::untie + */ bool -FGPropertyList::untieBool (const string &name) +SGPropertyList::untie (const string &name) { - FG_LOG(FG_GENERAL, FG_INFO, "Untying bool property '" << name << '\''); - return getValue(name, true)->untieBool(); + FG_LOG(FG_GENERAL, FG_INFO, "Untying property '" << name << '\''); + return getValue(name, true)->untie(); } -bool -FGPropertyList::untieInt (const string &name) + +//////////////////////////////////////////////////////////////////////// +// 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. + */ +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; +} + + +/** + * Constructor. + */ +SGPropertyNode::SGPropertyNode (const string &path, + SGPropertyList * props) + : _props(props), _node(0) { - FG_LOG(FG_GENERAL, FG_INFO, "Untying int property '" << name << '\''); - return getValue(name, true)->untieInt(); + setPath(path); } -bool -FGPropertyList::untieFloat (const string &name) +/** + * Destructor. + */ +SGPropertyNode::~SGPropertyNode () { - FG_LOG(FG_GENERAL, FG_INFO, "Untying float property '" << name << '\''); - return getValue(name, true)->untieFloat(); + delete _node; + _node = 0; } -bool -FGPropertyList::untieDouble (const string &name) +/** + * Set the path. + * + * Strip the trailing '/', if any. + */ +void +SGPropertyNode::setPath (const string &path) { - FG_LOG(FG_GENERAL, FG_INFO, "Untying double property '" << name << '\''); - return getValue(name, true)->untieDouble(); + _path = path; + + // Chop the final '/', if present. + if (_path.size() > 0 && _path[_path.size()-1] == '/') + _path.resize(_path.size()-1); } -bool -FGPropertyList::untieString (const string &name) +/** + * Return the local name of the property. + * + * The local name is just everything after the last slash. + */ +const string & +SGPropertyNode::getName () const { - FG_LOG(FG_GENERAL, FG_INFO, "Untying string property '" << name << '\''); - return getValue(name, true)->untieString(); + string::size_type pos = _path.rfind('/'); + if (pos != string::npos) { + _name = _path.substr(pos+1); + return _name; + } else { + return empty_string; + } } -void -FGPropertyList::dumpToLog () const +/** + * Return the number of children for the current node. + */ +int +SGPropertyNode::size () const +{ + if (_props == 0) + return 0; + + 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) { + s++; + lastBase = base; + } + it++; + } + + return s; +} + + +/** + * Initialize a node to represent this node's parent. + * + * A return value of true means success; otherwise, the node supplied + * is unmodified. + */ +SGPropertyNode & +SGPropertyNode::getParent () const +{ + 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; +} + + +/** + * Initialize a node to represent this node's nth child. + * + * A return value of true means success; otherwise, the node supplied + * is unmodified. + */ +SGPropertyNode & +SGPropertyNode::getChild (int n) const { - const_iterator it = FGPropertyList::begin(); - FG_LOG(FG_GENERAL, FG_INFO, "Begin property list dump..."); - while (it != end()) { - FG_LOG(FG_GENERAL, FG_INFO, "Property: " << it->first); + 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++; } - FG_LOG(FG_GENERAL, FG_INFO, "...End property list dump"); + + return *_node; +} + + +/** + * Return a node for an arbitrary subpath. + * + * Never returns 0. + */ +SGPropertyNode & +SGPropertyNode::getSubNode (const string &subpath) const +{ + if (_node == 0) + _node = new SGPropertyNode(); + + _node->setPropertyList(_props); + _node->setPath(_path + string("/") + subpath); + return *_node; +} + + +/** + * Test whether the specified subpath has a value. + */ +bool +SGPropertyNode::hasValue (const string &subpath) const +{ + if (_props == 0) + return false; + + if (subpath.size() == 0) + return _props->hasValue(_path); + else + return _props->hasValue(_path + string("/") + subpath); +} + + +/** + * 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. + */ +SGValue * +SGPropertyNode::getValue (const string &subpath) +{ + if (_props == 0) + return 0; + + if (subpath.size() == 0) + return _props->getValue(_path); + else + return _props->getValue(_path + string("/") + subpath); +} + + +/** + * Return a bool value. + */ +bool +SGPropertyNode::getBoolValue (const string &subpath, bool defaultValue) const +{ + if (_props == 0) + return defaultValue; + + if (subpath == "") + return _props->getBoolValue(_path, defaultValue); + else + return _props->getBoolValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return an int value. + */ +int +SGPropertyNode::getIntValue (const string &subpath, int defaultValue) const +{ + if (_props == 0) + return defaultValue; + + if (subpath == "") + return _props->getIntValue(_path, defaultValue); + else + return _props->getIntValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return a float value. + */ +float +SGPropertyNode::getFloatValue (const string &subpath, float defaultValue) const +{ + if (_props == 0) + return defaultValue; + + if (subpath == "") + return _props->getFloatValue(_path, defaultValue); + else + return _props->getFloatValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return a double value. + */ +double +SGPropertyNode::getDoubleValue (const string &subpath, + double defaultValue) const +{ + if (_props == 0) + return defaultValue; + + if (subpath == "") + return _props->getDoubleValue(_path, defaultValue); + else + return _props->getDoubleValue(_path + string("/") + subpath, + defaultValue); } +/** + * Return a string value. + */ +const string & +SGPropertyNode::getStringValue (const string &subpath, + const string &defaultValue) const +{ + if (_props == 0) + return defaultValue; + + if (subpath == "") + return _props->getStringValue(_path, defaultValue); + else + return _props->getStringValue(_path + string("/") + subpath, + defaultValue); +} + // end of props.cxx