Rewrite of the property manager my David Megginson.

This commit is contained in:
curt
2000-12-19 21:53:37 +00:00
parent 1b129b289c
commit 8b13d71fcf
5 changed files with 1857 additions and 1550 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 << "&amp;";
@@ -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
View 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;
}
}