9/05/2000 updates from David Megginson relating to the property manager and

easyxml support.  This is a bit of simgear side support to facilitate a
configurable instrument panel in flightgear.
This commit is contained in:
curt
2000-09-05 21:38:00 +00:00
parent 25b743bdec
commit a1bf8d2229
5 changed files with 578 additions and 69 deletions

View File

@@ -21,6 +21,7 @@ libsgmisc_a_SOURCES = \
fgpath.cxx \
fgstream.cxx \
props.cxx \
props_io.cxx \
strutils.cxx \
texcoord.cxx \
zfstream.cxx

View File

@@ -304,8 +304,6 @@ float
SGValue::getFloatValue () const
{
switch (_type) {
case UNKNOWN:
return 0.0;
case BOOL:
return (float)(getRawBool());
case INT:
@@ -314,6 +312,7 @@ SGValue::getFloatValue () const
return getRawFloat();
case DOUBLE:
return (float)(getRawDouble());
case UNKNOWN:
case STRING:
return (float)atof(getRawString().c_str());
}
@@ -330,8 +329,6 @@ double
SGValue::getDoubleValue () const
{
switch (_type) {
case UNKNOWN:
return 0.0;
case BOOL:
return (double)(getRawBool());
case INT:
@@ -340,6 +337,7 @@ SGValue::getDoubleValue () const
return (double)(getRawFloat());
case DOUBLE:
return getRawDouble();
case UNKNOWN:
case STRING:
return atof(getRawString().c_str());
}
@@ -357,8 +355,6 @@ SGValue::getStringValue () const
{
char buf[512];
switch (_type) {
case UNKNOWN:
return getRawString();
case BOOL:
if (getRawBool())
string_val = "true";
@@ -377,6 +373,7 @@ SGValue::getStringValue () const
sprintf(buf, "%f", getRawDouble());
string_val = buf;
return string_val;
case UNKNOWN:
case STRING:
return getRawString();
}
@@ -739,11 +736,11 @@ SGPropertyList::getValue (const string &name) const
* better to get the SGValue and query it repeatedly.
*/
bool
SGPropertyList::getBoolValue (const string &name) const
SGPropertyList::getBoolValue (const string &name, bool defaultValue) const
{
const SGValue * val = getValue(name);
if (val == 0)
return false;
return defaultValue;
else
return val->getBoolValue();
}
@@ -756,11 +753,11 @@ SGPropertyList::getBoolValue (const string &name) const
* better to get the SGValue and query it repeatedly.
*/
int
SGPropertyList::getIntValue (const string &name) const
SGPropertyList::getIntValue (const string &name, int defaultValue) const
{
const SGValue * val = getValue(name);
if (val == 0)
return 0;
return defaultValue;
else
return val->getIntValue();
}
@@ -773,11 +770,11 @@ SGPropertyList::getIntValue (const string &name) const
* better to get the SGValue and query it repeatedly.
*/
float
SGPropertyList::getFloatValue (const string &name) const
SGPropertyList::getFloatValue (const string &name, float defaultValue) const
{
const SGValue * val = getValue(name);
if (val == 0)
return 0.0;
return defaultValue;
else
return val->getFloatValue();
}
@@ -790,11 +787,11 @@ SGPropertyList::getFloatValue (const string &name) const
* better to get the SGValue and query it repeatedly.
*/
double
SGPropertyList::getDoubleValue (const string &name) const
SGPropertyList::getDoubleValue (const string &name, double defaultValue) const
{
const SGValue * val = getValue(name);
if (val == 0)
return 0.0;
return defaultValue;
else
return val->getDoubleValue();
}
@@ -807,11 +804,12 @@ SGPropertyList::getDoubleValue (const string &name) const
* better to save the SGValue and query it repeatedly.
*/
const string &
SGPropertyList::getStringValue (const string &name) const
SGPropertyList::getStringValue (const string &name,
const string &defaultValue) const
{
const SGValue * val = getValue(name);
if (val == 0)
return empty_string;
return defaultValue;
else
return val->getStringValue();
}
@@ -1041,7 +1039,7 @@ get_base (const string &parent, const string &child,
*/
SGPropertyNode::SGPropertyNode (const string &path = "",
SGPropertyList * props = 0)
: _props(props)
: _props(props), _node(0)
{
setPath(path);
}
@@ -1052,6 +1050,8 @@ SGPropertyNode::SGPropertyNode (const string &path = "",
*/
SGPropertyNode::~SGPropertyNode ()
{
delete _node;
_node = 0;
}
@@ -1089,24 +1089,6 @@ SGPropertyNode::getName () const
}
/**
* Return the value of the current node.
*
* Currently, this does a lookup each time, but we could cache the
* value safely as long as it's non-zero.
*
* Note that this will not create the value if it doesn't already exist.
*/
SGValue *
SGPropertyNode::getValue ()
{
if (_props == 0 || _path.size() == 0)
return 0;
else
return _props->getValue(_path);
}
/**
* Return the number of children for the current node.
*/
@@ -1143,17 +1125,18 @@ SGPropertyNode::size () const
* A return value of true means success; otherwise, the node supplied
* is unmodified.
*/
bool
SGPropertyNode::getParent (SGPropertyNode &parent) const
SGPropertyNode &
SGPropertyNode::getParent () const
{
if (_node == 0)
_node = new SGPropertyNode();
string::size_type pos = _path.rfind('/');
if (pos != string::npos) {
parent.setPath(_path.substr(0, pos-1));
parent.setPropertyList(_props);
return true;
} else {
return false;
_node->setPropertyList(_props);
_node->setPath(_path.substr(0, pos-1));
}
return *_node;
}
@@ -1163,11 +1146,14 @@ SGPropertyNode::getParent (SGPropertyNode &parent) const
* A return value of true means success; otherwise, the node supplied
* is unmodified.
*/
bool
SGPropertyNode::getChild (SGPropertyNode &child, int n) const
SGPropertyNode &
SGPropertyNode::getChild (int n) const
{
if (_node == 0)
_node = new SGPropertyNode();
if (_props == 0)
return false;
return *_node;
int s = 0;
string base;
@@ -1180,12 +1166,9 @@ SGPropertyNode::getChild (SGPropertyNode &child, int n) const
while (it != end) {
if (get_base(pattern, it->first, base) && base != lastBase) {
if (s == n) {
string path = _path;
path += '/';
path += base;
child.setPath(path);
child.setPropertyList(_props);
return true;
_node->setPropertyList(_props);
_node->setPath(_path + string("/") + base);
return *_node;
} else {
s++;
lastBase = base;
@@ -1194,7 +1177,133 @@ SGPropertyNode::getChild (SGPropertyNode &child, int n) const
it++;
}
return false;
return *_node;
}
/**
* Return a node for an arbitrary subpath.
*
* Never returns 0.
*/
SGPropertyNode &
SGPropertyNode::getSubNode (const string &subpath) const
{
if (_node == 0)
_node = new SGPropertyNode();
_node->setPropertyList(_props);
_node->setPath(_path + string("/") + subpath);
return *_node;
}
/**
* Return the value of the current node.
*
* Currently, this does a lookup each time, but we could cache the
* value safely as long as it's non-zero.
*
* Note that this will not create the value if it doesn't already exist.
*/
SGValue *
SGPropertyNode::getValue (const string &subpath)
{
if (_props == 0 || _path.size() == 0)
return 0;
if (subpath.size() == 0)
return _props->getValue(_path);
else
return _props->getValue(_path + string("/") + subpath);
}
/**
* Return a bool value.
*/
bool
SGPropertyNode::getBoolValue (const string &subpath, bool defaultValue) const
{
if (_props == 0 || _path.size() == 0)
return defaultValue;
if (subpath == "")
return _props->getBoolValue(_path, defaultValue);
else
return _props->getBoolValue(_path + string("/") + subpath,
defaultValue);
}
/**
* Return an int value.
*/
int
SGPropertyNode::getIntValue (const string &subpath, int defaultValue) const
{
if (_props == 0 || _path.size() == 0)
return defaultValue;
if (subpath == "")
return _props->getIntValue(_path, defaultValue);
else
return _props->getIntValue(_path + string("/") + subpath,
defaultValue);
}
/**
* Return a float value.
*/
float
SGPropertyNode::getFloatValue (const string &subpath, float defaultValue) const
{
if (_props == 0 || _path.size() == 0)
return defaultValue;
if (subpath == "")
return _props->getFloatValue(_path, defaultValue);
else
return _props->getFloatValue(_path + string("/") + subpath,
defaultValue);
}
/**
* Return a double value.
*/
double
SGPropertyNode::getDoubleValue (const string &subpath,
double defaultValue) const
{
if (_props == 0 || _path.size() == 0)
return defaultValue;
if (subpath == "")
return _props->getDoubleValue(_path, defaultValue);
else
return _props->getDoubleValue(_path + string("/") + subpath,
defaultValue);
}
/**
* Return a string value.
*/
const string &
SGPropertyNode::getStringValue (const string &subpath,
const string &defaultValue) const
{
if (_props == 0 || _path.size() == 0)
return defaultValue;
if (subpath == "")
return _props->getStringValue(_path, defaultValue);
else
return _props->getStringValue(_path + string("/") + subpath,
defaultValue);
}
// end of props.cxx

View File

@@ -19,9 +19,42 @@
#include <string>
#include <map>
#include <iostream>
using std::string;
using std::map;
using std::istream;
using std::ostream;
#ifdef UNKNOWN
#pragma warn A sloppy coder has defined UNKNOWN as a macro!
#undef UNKNOWN
#endif
#ifdef BOOL
#pragma warn A sloppy coder has defined BOOL as a macro!
#undef BOOL
#endif
#ifdef INT
#pragma warn A sloppy coder has defined INT as a macro!
#undef INT
#endif
#ifdef FLOAT
#pragma warn A sloppy coder has defined FLOAT as a macro!
#undef FLOAT
#endif
#ifdef DOUBLE
#pragma warn A sloppy coder has defined DOUBLE as a macro!
#undef DOUBLE
#endif
#ifdef STRING
#pragma warn A sloppy coder has defined STRING as a macro!
#undef STRING
#endif
@@ -80,7 +113,7 @@ public:
virtual int getIntValue () const;
virtual float getFloatValue () const;
virtual double getDoubleValue () const;
virtual const string &getStringValue () const;
virtual const string & getStringValue () const;
// Setters.
virtual bool setBoolValue (bool value);
@@ -231,11 +264,17 @@ public:
virtual SGValue * getValue (const string &name, bool create = false);
virtual const SGValue * getValue (const string &name) const;
virtual bool getBoolValue (const string &name) const;
virtual int getIntValue (const string &name) const;
virtual float getFloatValue (const string &name) const;
virtual double getDoubleValue (const string &name) const;
virtual const string &getStringValue (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);
@@ -331,17 +370,45 @@ public:
// Accessors for derived information.
virtual int size () const;
virtual const string &getName () const;
virtual SGValue * getValue ();
virtual bool getParent (SGPropertyNode &parent) const;
virtual bool getChild (SGPropertyNode &child, int n) const;
virtual SGPropertyNode &getParent () const;
virtual SGPropertyNode &getChild (int n) const;
virtual SGPropertyNode &getSubNode (const string &subpath) const;
// Get values directly.
virtual SGValue * getValue (const string &subpath = "");
virtual bool getBoolValue (const string &subpath = "",
bool defaultValue = false) const;
virtual int getIntValue (const string &subpath = "",
int defaultValue = 0) const;
virtual float getFloatValue (const string &subpath = "",
float defaultValue = 0.0) const;
virtual double getDoubleValue (const string &subpath = "",
double defaultValue = 0.0L) const;
virtual const string &
getStringValue (const string &subpath = "",
const string &defaultValue = "") const;
private:
string _path;
mutable string _name; // for pointer persistence only
SGPropertyList * _props;
// for pointer persistence...
// NOT THREAD SAFE!!!
// (each thread must have its own node
// object)
mutable string _name;
mutable SGPropertyNode * _node;
};
////////////////////////////////////////////////////////////////////////
// Input and output.
////////////////////////////////////////////////////////////////////////
extern bool readPropertyList (istream &input, SGPropertyList * props);
extern bool writePropertyList (ostream &output, const SGPropertyList * props);
////////////////////////////////////////////////////////////////////////
// Global property manager.

332
simgear/misc/props_io.cxx Normal file
View File

@@ -0,0 +1,332 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/debug/logstream.hxx>
#include <simgear/xml/easyxml.hxx>
#include "props.hxx"
#include <iostream>
#include <string>
#include <vector>
using std::istream;
using std::ostream;
using std::string;
using std::vector;
////////////////////////////////////////////////////////////////////////
// Visitor class for building the property list.
////////////////////////////////////////////////////////////////////////
class PropVisitor : public XMLVisitor
{
public:
PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {}
void startDocument ();
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);
bool isOK () const { return _ok; }
private:
void pushState (const char * name) {
_states.push_back(_state);
_level++;
_state.name = name;
_state.type = SGValue::UNKNOWN;
_state.data = "";
_state.hasChildren = false;
_state.hasData = false;
}
void popState () {
_state = _states.back();
_states.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;
};
void
PropVisitor::startDocument ()
{
_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");
}
void
PropVisitor::endElement (const char * name)
{
if (!_ok)
return;
// See if there's a property to add.
if (_state.hasData) {
bool status = false;
// Figure out the path name.
string path = "";
for (int i = 2; i < _level; i++) {
path += '/';
path += _states[i].name;
}
path += '/';
path += _state.name;
// Set the value
switch (_state.type) {
case SGValue::BOOL:
if (_state.data == "true" || _state.data == "TRUE") {
status = _props->setBoolValue(path, true);
} else if (atof(_state.data.c_str()) != 0.0) {
status = _props->setBoolValue(path, true);
} else {
status =_props->setBoolValue(path, false);
}
break;
case SGValue::INT :
status = _props->setIntValue(path, atoi(_state.data.c_str()));
break;
case SGValue::FLOAT:
status = _props->setFloatValue(path, atof(_state.data.c_str()));
break;
case SGValue::DOUBLE:
status = _props->setDoubleValue(path, atof(_state.data.c_str()));
break;
case SGValue::STRING:
status = _props->setStringValue(path, _state.data);
break;
default:
status = _props->setUnknownValue(path, _state.data);
break;
}
if (!status)
FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
<< path << " to " << _state.data);
}
// Pop the stack.
popState();
}
void
PropVisitor::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
}
void
PropVisitor::warning (const char * message, int line, int col)
{
FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
<< message << " (" << line << ',' << col << ')');
}
void
PropVisitor::error (const char * message, int line, int col)
{
FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
<< message << " (" << line << ',' << col << ')');
_ok = false;
}
////////////////////////////////////////////////////////////////////////
// Property list reader.
////////////////////////////////////////////////////////////////////////
bool
readPropertyList (istream &input, SGPropertyList * props)
{
PropVisitor visitor(props);
return readXML(input, visitor) && visitor.isOK();
}
////////////////////////////////////////////////////////////////////////
// Property list writer.
////////////////////////////////////////////////////////////////////////
#define INDENT_STEP 2
/**
* Return the type name.
*/
static const char *
getTypeName (SGValue::Type type)
{
switch (type) {
case SGValue::UNKNOWN:
return "unknown";
case SGValue::BOOL:
return "bool";
case SGValue::INT:
return "int";
case SGValue::FLOAT:
return "float";
case SGValue::DOUBLE:
return "double";
case SGValue::STRING:
return "string";
}
}
/**
* Escape characters for output.
*/
static void
writeData (ostream &output, const string &data)
{
for (int i = 0; i < data.size(); i++) {
switch (data[i]) {
case '&':
output << "&amp;";
break;
case '<':
output << "&lt;";
break;
case '>':
output << "&gt;";
break;
default:
output << data[i];
break;
}
}
}
static void
doIndent (ostream &output, int indent)
{
while (indent-- > 0) {
output << ' ';
}
}
static bool
writeNode (ostream &output, SGPropertyNode node, int indent)
{
const string &name = node.getName();
int size = node.size();
// Write out the literal value, if any.
SGValue * value = node.getValue();
if (value != 0) {
SGValue::Type type = value->getType();
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) {
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;
}
}
bool
writePropertyList (ostream &output, const SGPropertyList * props)
{
SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
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);
}
output << "</PropertyList>" << endl;
}

View File

@@ -20,9 +20,10 @@ int
XMLAttributes::findAttribute (const char * name) const
{
int s = size();
for (int i = 0; i < s; i++)
for (int i = 0; i < s; i++) {
if (strcmp(name, getName(i)) == 0)
return i;
}
return -1;
}
@@ -39,7 +40,7 @@ XMLAttributes::getValue (const char * name) const
if (pos >= 0)
return getValue(pos);
else
return getValue(0);
return 0;
}
@@ -122,9 +123,9 @@ class ExpatAtts : public XMLAttributes
public:
ExpatAtts (const char ** atts) : _atts(atts) {}
int size () const;
const char * getName (int i) const;
const char * getValue (int i) const;
virtual int size () const;
virtual const char * getName (int i) const;
virtual const char * getValue (int i) const;
private:
const char ** _atts;
@@ -162,8 +163,7 @@ ExpatAtts::getValue (int i) const
static void
start_element (void * userData, const char * name, const char ** atts)
{
ExpatAtts attributes(atts);
VISITOR.startElement(name, attributes);
VISITOR.startElement(name, ExpatAtts(atts));
}
static void