Rewrite of the property manager my David Megginson.
This commit is contained in:
@@ -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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <stdio.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
|
||||
@@ -59,146 +53,287 @@ 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.
|
||||
*
|
||||
* 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.
|
||||
* Unlike values, raw values are not persistent -- the raw value can
|
||||
* change frequently, but the changes are not visible to the application.
|
||||
*
|
||||
* Values also have attributes that control whether they can be read
|
||||
* from, written to, or archived (i.e. saved to disk).
|
||||
* 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.
|
||||
*
|
||||
* All raw values must implement getValue, setValue, and clone for the
|
||||
* appropriate type.
|
||||
*/
|
||||
template <class T>
|
||||
class SGRawValue
|
||||
{
|
||||
public:
|
||||
static const T DefaultValue; // Default for this kind of raw value.
|
||||
|
||||
SGRawValue () {}
|
||||
virtual ~SGRawValue () {}
|
||||
virtual T getValue () const = 0;
|
||||
virtual bool setValue (T value) = 0;
|
||||
virtual SGRawValue * clone () const = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 T>
|
||||
class SGRawValueInternal : public SGRawValue<T>
|
||||
{
|
||||
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<T> * clone () const {
|
||||
return new SGRawValueInternal<T>(_value);
|
||||
}
|
||||
private:
|
||||
T _value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 T>
|
||||
class SGRawValuePointer : public SGRawValue<T>
|
||||
{
|
||||
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<T> * clone () const {
|
||||
return new SGRawValuePointer<T>(_ptr);
|
||||
}
|
||||
private:
|
||||
T * _ptr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A value managed through static functions.
|
||||
*
|
||||
* A read-only value will not have a setter; a write-only value will
|
||||
* not have a getter.
|
||||
*/
|
||||
template <class T>
|
||||
class SGRawValueFunctions : public SGRawValue<T>
|
||||
{
|
||||
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<T> * clone () const {
|
||||
return new SGRawValueFunctions<T>(_getter,_setter);
|
||||
}
|
||||
private:
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 T>
|
||||
class SGRawValueFunctionsIndexed : public SGRawValue<T>
|
||||
{
|
||||
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<T> * clone () const {
|
||||
return new SGRawValueFunctionsIndexed<T>(_index, _getter, _setter);
|
||||
}
|
||||
private:
|
||||
int _index;
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A value managed through an object and access methods.
|
||||
*
|
||||
* A read-only value will not have a setter; a write-only value will
|
||||
* not have a getter.
|
||||
*/
|
||||
template <class C, class T>
|
||||
class SGRawValueMethods : public SGRawValue<T>
|
||||
{
|
||||
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<T> * clone () const {
|
||||
return new SGRawValueMethods<C,T>(_obj, _getter, _setter);
|
||||
}
|
||||
private:
|
||||
C &_obj;
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An indexed value managed through an object and access methods.
|
||||
*
|
||||
* A read-only value will not have a setter; a write-only value will
|
||||
* not have a getter.
|
||||
*/
|
||||
template <class C, class T>
|
||||
class SGRawValueMethodsIndexed : public SGRawValue<T>
|
||||
{
|
||||
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<T> * clone () const {
|
||||
return new SGRawValueMethodsIndexed<C,T>(_obj, _index, _getter, _setter);
|
||||
}
|
||||
private:
|
||||
C &_obj;
|
||||
int _index;
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SGValue
|
||||
{
|
||||
public:
|
||||
|
||||
// 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 &);
|
||||
|
||||
enum Type {
|
||||
UNKNOWN, // no value assigned yet
|
||||
BOOL, // boolean
|
||||
INT, // integer
|
||||
FLOAT, // floating point
|
||||
DOUBLE, // double precision
|
||||
STRING // text
|
||||
BOOL,
|
||||
INT,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
SGValue ();
|
||||
virtual ~SGValue ();
|
||||
SGValue (const SGValue &value);
|
||||
~SGValue ();
|
||||
|
||||
// Meta information.
|
||||
virtual Type getType () const { return _type; }
|
||||
virtual bool isTied () const { return _tied; }
|
||||
Type getType () const { return _type; }
|
||||
|
||||
// Accessors.
|
||||
virtual bool getBoolValue () const;
|
||||
virtual int getIntValue () const;
|
||||
virtual float getFloatValue () const;
|
||||
virtual double getDoubleValue () const;
|
||||
virtual const string & getStringValue () const;
|
||||
bool getBoolValue () const;
|
||||
int getIntValue () const;
|
||||
float getFloatValue () const;
|
||||
double getDoubleValue () 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);
|
||||
bool setBoolValue (bool value);
|
||||
bool setIntValue (int value);
|
||||
bool setFloatValue (float value);
|
||||
bool setDoubleValue (double value);
|
||||
bool setStringValue (string value);
|
||||
bool setUnknownValue (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);
|
||||
bool isTied () const { return _tied; }
|
||||
|
||||
// Untie from external variables.
|
||||
virtual bool untie ();
|
||||
bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<string> &rawValue, bool useDefault = true);
|
||||
|
||||
protected:
|
||||
|
||||
bool getRawBool () const;
|
||||
int getRawInt () const;
|
||||
float getRawFloat () const;
|
||||
double getRawDouble () const;
|
||||
const string &getRawString () const;
|
||||
|
||||
bool setRawBool (bool value);
|
||||
bool setRawInt (int value);
|
||||
bool setRawFloat (float value);
|
||||
bool setRawDouble (double value);
|
||||
bool setRawString (const string & value);
|
||||
bool untie ();
|
||||
|
||||
private:
|
||||
|
||||
void clear_value ();
|
||||
|
||||
Type _type;
|
||||
bool _tied;
|
||||
|
||||
mutable string string_val;
|
||||
|
||||
// The value is one of the following...
|
||||
// The right kind of pointer...
|
||||
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;
|
||||
|
||||
SGRawValue<bool> * bool_val;
|
||||
SGRawValue<int> * int_val;
|
||||
SGRawValue<float> * float_val;
|
||||
SGRawValue<double> * double_val;
|
||||
SGRawValue<string> * string_val;
|
||||
} _value;
|
||||
|
||||
};
|
||||
@@ -206,223 +341,138 @@ private:
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Top-level manager.
|
||||
// A node in a property tree.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* 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<string,SGValue>
|
||||
*
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
typedef map<string, SGValue> 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);
|
||||
|
||||
private:
|
||||
value_map _props;
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Tree/node/directory view.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
// Accessor and setter for the real
|
||||
// property list.
|
||||
virtual SGPropertyList * getPropertyList () { return _props; }
|
||||
virtual void setPropertyList (SGPropertyList * props) {
|
||||
_props = props;
|
||||
}
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
vector<SGPropertyNode *> getChildren (const string &name);
|
||||
vector<const SGPropertyNode *> getChildren (const string &name) const;
|
||||
|
||||
// Check for a value.
|
||||
virtual bool hasValue (const string &subpath = "") const;
|
||||
// Path information.
|
||||
string getPath (bool simplify = false) 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;
|
||||
// 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<bool> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
|
||||
bool tie (const SGRawValue<string> &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<bool> &rawValue,
|
||||
bool useDefault = true);
|
||||
bool tie (const string &relative_path, const SGRawValue<int> &rawValue,
|
||||
bool useDefault = true);
|
||||
bool tie (const string &relative_path, const SGRawValue<float> &rawValue,
|
||||
bool useDefault = true);
|
||||
bool tie (const string &relative_path, const SGRawValue<double> &rawValue,
|
||||
bool useDefault = true);
|
||||
bool tie (const string &relative_path, const SGRawValue<string> &rawValue,
|
||||
bool useDefault = true);
|
||||
|
||||
bool untie (const string &relative_path);
|
||||
|
||||
protected:
|
||||
|
||||
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) {}
|
||||
|
||||
SGValue * _value;
|
||||
string _name;
|
||||
int _index;
|
||||
SGPropertyNode * _parent;
|
||||
vector<SGPropertyNode *> _children;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Input and output.
|
||||
// I/O functions.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h> // strcmp()
|
||||
#include <stdlib.h> // atof() atoi()
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
@@ -16,200 +15,159 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<State> _states;
|
||||
int _level;
|
||||
bool _ok;
|
||||
string _data;
|
||||
SGPropertyNode * _root;
|
||||
int _level;
|
||||
vector<State> _state_stack;
|
||||
|
||||
};
|
||||
|
||||
void
|
||||
PropVisitor::startDocument ()
|
||||
PropsVisitor::startXML ()
|
||||
{
|
||||
_level = 0;
|
||||
_ok = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PropVisitor::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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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");
|
||||
_state_stack.resize(0);
|
||||
}
|
||||
|
||||
void
|
||||
PropVisitor::endElement (const char * name)
|
||||
PropsVisitor::endXML ()
|
||||
{
|
||||
if (!_ok)
|
||||
return;
|
||||
_level = 0;
|
||||
_state_stack.resize(0);
|
||||
}
|
||||
|
||||
// See if there's a property to add.
|
||||
if (_state.hasData) {
|
||||
bool status = false;
|
||||
void
|
||||
PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
|
||||
{
|
||||
if (_level == 0) {
|
||||
push_state(_root, "");
|
||||
}
|
||||
|
||||
// Figure out the path name.
|
||||
string path = "";
|
||||
for (int i = 2; i < _level; i++) {
|
||||
path += '/';
|
||||
path += _states[i].name;
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PropsVisitor::endElement (const char * name)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 << " n=\"" << index
|
||||
<< "\" type=\"" << getTypeName(node->getType()) << "\">";
|
||||
writeData(output, node->getStringValue());
|
||||
output << "</" << name << '>' << endl;;
|
||||
}
|
||||
|
||||
// If there are children, write them
|
||||
// next.
|
||||
if (nChildren > 0) {
|
||||
doIndent(output, indent);
|
||||
output << '<' << name << " n=\"" << index << "\">" << endl;;
|
||||
for (int i = 0; i < nChildren; i++)
|
||||
writeNode(output, node->getChild(i), indent + INDENT_STEP);
|
||||
doIndent(output, indent);
|
||||
output << '<' << name;
|
||||
if (type != SGValue::UNKNOWN)
|
||||
output << " type=\"" << getTypeName(type) << '"';
|
||||
output << '>';
|
||||
writeData(output, value->getStringValue());
|
||||
output << "</" << name << '>' << endl;
|
||||
}
|
||||
|
||||
// Write out the children, if any.
|
||||
if (size > 0) {
|
||||
// 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 << '>' << endl;;
|
||||
for (int i = 0; i < size; i++) {
|
||||
writeNode(output, node.getChild(i), indent + INDENT_STEP);
|
||||
}
|
||||
doIndent(output, indent);
|
||||
output << "</" << name << '>' << endl;
|
||||
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 << "<?xml version=\"1.0\"?>" << endl << endl;
|
||||
output << "<PropertyList>" << 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 << "</PropertyList>" << 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;
|
||||
}
|
||||
|
||||
338
simgear/misc/props_test.cxx
Normal file
338
simgear/misc/props_test.cxx
Normal file
@@ -0,0 +1,338 @@
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Test harness.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <iostream>
|
||||
#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<int>(&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<int>(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<double>(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<class Stuff,float> 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<class Stuff, float>
|
||||
(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<SGPropertyNode *> 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user