fgpath.cxx \
fgstream.cxx \
props.cxx \
+ props_io.cxx \
strutils.cxx \
texcoord.cxx \
zfstream.cxx
SGValue::getFloatValue () const
{
switch (_type) {
- case UNKNOWN:
- return 0.0;
case BOOL:
return (float)(getRawBool());
case INT:
return getRawFloat();
case DOUBLE:
return (float)(getRawDouble());
+ case UNKNOWN:
case STRING:
return (float)atof(getRawString().c_str());
}
SGValue::getDoubleValue () const
{
switch (_type) {
- case UNKNOWN:
- return 0.0;
case BOOL:
return (double)(getRawBool());
case INT:
return (double)(getRawFloat());
case DOUBLE:
return getRawDouble();
+ case UNKNOWN:
case STRING:
return atof(getRawString().c_str());
}
{
char buf[512];
switch (_type) {
- case UNKNOWN:
- return getRawString();
case BOOL:
if (getRawBool())
string_val = "true";
sprintf(buf, "%f", getRawDouble());
string_val = buf;
return string_val;
+ case UNKNOWN:
case STRING:
return getRawString();
}
* 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();
}
* 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();
}
* 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();
}
* 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();
}
* 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();
}
*/
SGPropertyNode::SGPropertyNode (const string &path = "",
SGPropertyList * props = 0)
- : _props(props)
+ : _props(props), _node(0)
{
setPath(path);
}
*/
SGPropertyNode::~SGPropertyNode ()
{
+ delete _node;
+ _node = 0;
}
}
-/**
- * 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.
*/
* 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;
}
* 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;
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;
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
#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
\f
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);
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);
// 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;
};
+\f
+////////////////////////////////////////////////////////////////////////
+// Input and output.
+////////////////////////////////////////////////////////////////////////
+
+extern bool readPropertyList (istream &input, SGPropertyList * props);
+extern bool writePropertyList (ostream &output, const SGPropertyList * props);
+
+
\f
////////////////////////////////////////////////////////////////////////
// Global property manager.
--- /dev/null
+
+#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;
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list reader.
+////////////////////////////////////////////////////////////////////////
+
+bool
+readPropertyList (istream &input, SGPropertyList * props)
+{
+ PropVisitor visitor(props);
+ return readXML(input, visitor) && visitor.isOK();
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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 << "&";
+ break;
+ case '<':
+ output << "<";
+ break;
+ case '>':
+ output << ">";
+ 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;
+}
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;
}
if (pos >= 0)
return getValue(pos);
else
- return getValue(0);
+ return 0;
}
\f
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;
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