X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Fprops_io.cxx;h=160539ae73b41309c9f6d1fe88b9bdeada65976c;hb=5bab565cfe4c30d6cf08ecaba50af74d5e4f0c98;hp=d4ea27ded822f2425793ee6e3f9bb7f8fa35ac2a;hpb=557fade4a7df3f030b943ba6114c3c566c57b73c;p=simgear.git diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx index d4ea27de..160539ae 100644 --- a/simgear/misc/props_io.cxx +++ b/simgear/misc/props_io.cxx @@ -1,16 +1,13 @@ -#ifdef HAVE_CONFIG_H -# include -#endif - #include #include // atof() atoi() +#include #include #include -#include "fgpath.hxx" +#include "sg_path.hxx" #include "props.hxx" #include STL_IOSTREAM @@ -33,6 +30,8 @@ SG_USING_STD(string); SG_USING_STD(vector); SG_USING_STD(map); +#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE) + //////////////////////////////////////////////////////////////////////// @@ -44,7 +43,9 @@ class PropsVisitor : public XMLVisitor public: PropsVisitor (SGPropertyNode * root, const string &base) - : _ok(true), _root(root), _level(0), _base(base) {} + : _root(root), _level(0), _base(base), _hasException(false) {} + + virtual ~PropsVisitor () {} void startXML (); void endXML (); @@ -52,29 +53,34 @@ public: void endElement (const char * name); void data (const char * s, int length); void warning (const char * message, int line, int column); - void error (const char * message, int line, int column); - bool isOK () const { return _ok; } + bool hasException () const { return _hasException; } + sg_io_exception &getException () { return _exception; } + void setException (const sg_io_exception &exception) { + _exception = exception; + _hasException = true; + } private: struct State { - State () : node(0), type("") {} - State (SGPropertyNode * _node, const char * _type) - : node(_node), type(_type) {} + State () : node(0), type(""), mode(DEFAULT_MODE) {} + State (SGPropertyNode * _node, const char * _type, int _mode) + : node(_node), type(_type), mode(_mode) {} SGPropertyNode * node; string type; + int mode; map counters; }; State &state () { return _state_stack[_state_stack.size() - 1]; } - void push_state (SGPropertyNode * node, const char * type) { + void push_state (SGPropertyNode * node, const char * type, int mode) { if (type == 0) - _state_stack.push_back(State(node, "unknown")); + _state_stack.push_back(State(node, "unspecified", mode)); else - _state_stack.push_back(State(node, type)); + _state_stack.push_back(State(node, type, mode)); _level++; _data = ""; } @@ -84,12 +90,13 @@ private: _level--; } - bool _ok; string _data; SGPropertyNode * _root; int _level; vector _state_stack; string _base; + sg_io_exception _exception; + bool _hasException; }; void @@ -106,55 +113,101 @@ PropsVisitor::endXML () _state_stack.resize(0); } + +/** + * Check a yes/no flag, with default. + */ +static bool +checkFlag (const char * flag, bool defaultState = true) +{ + if (flag == 0) + return defaultState; + else if (string(flag) == "y") + return true; + else if (string(flag) == "n") + return false; + else { + string message = "Unrecognized flag value '"; + message += flag; + message += '\''; + // FIXME: add location info + throw sg_io_exception(message, "SimGear Property Reader"); + } +} + void PropsVisitor::startElement (const char * name, const XMLAttributes &atts) { State &st = state(); if (_level == 0) { - if (string(name) != "PropertyList") { - SG_LOG(SG_INPUT, SG_ALERT, "Root element name is " << - name << "; expected PropertyList"); - _ok = false; + if (string(name) != (string)"PropertyList") { + string message = "Root element name is "; + message += name; + message += "; expected PropertyList"; + throw sg_io_exception(message, "SimGear Property Reader"); } - push_state(_root, ""); + push_state(_root, "", DEFAULT_MODE); } else { + + const char * attval; // Get the index. - const char * att_n = atts.getValue("n"); + attval = atts.getValue("n"); int index = 0; - if (att_n != 0) { - index = atoi(att_n); - st.counters[name] = max(st.counters[name], index+1); + if (attval != 0) { + index = atoi(attval); + st.counters[name] = SG_MAX2(st.counters[name], index+1); } else { index = st.counters[name]; st.counters[name]++; } - // Check for an alias. + // Got the index, so grab the node. SGPropertyNode * node = st.node->getChild(name, index, true); - const char * att_alias = atts.getValue("alias"); - if (att_alias != 0) { - if (!node->alias(att_alias)) - SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << att_alias); + + // Get the access-mode attributes, + // but don't set yet (in case they + // prevent us from recording the value). + int mode = 0; + + attval = atts.getValue("read"); + if (checkFlag(attval, true)) + mode |= SGPropertyNode::READ; + attval = atts.getValue("write"); + if (checkFlag(attval, true)) + mode |= SGPropertyNode::WRITE; + attval = atts.getValue("archive"); + if (checkFlag(attval, false)) + mode |= SGPropertyNode::ARCHIVE; + attval = atts.getValue("trace-read"); + if (checkFlag(attval, false)) + mode |= SGPropertyNode::TRACE_READ; + attval = atts.getValue("trace-write"); + if (checkFlag(attval, false)) + mode |= SGPropertyNode::TRACE_WRITE; + + // Check for an alias. + attval = atts.getValue("alias"); + if (attval != 0) { + if (!node->alias(attval)) + SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval); } // Check for an include. - const char * att_include = atts.getValue("include"); - if (att_include != 0) { - FGPath path(FGPath(_base).dir()); - cerr << "Base is " << _base << endl; - cerr << "Dir is " << FGPath(_base).dir() << endl; - path.append(att_include); - if (!readProperties(path.str(), node)) { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to read include file " - << att_include); - _ok = false; + attval = atts.getValue("include"); + if (attval != 0) { + SGPath path(SGPath(_base).dir()); + path.append(attval); + try { + readProperties(path.str(), node); + } catch (sg_io_exception &e) { + setException(e); } } - push_state(node, atts.getValue("type")); + push_state(node, atts.getValue("type"), mode); } } @@ -174,18 +227,22 @@ PropsVisitor::endElement (const char * name) ret = st.node->setBoolValue(false); } else if (st.type == "int") { ret = st.node->setIntValue(atoi(_data.c_str())); + } else if (st.type == "long") { + ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0)); } 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())); + ret = st.node->setDoubleValue(strtod(_data.c_str(), 0)); } else if (st.type == "string") { ret = st.node->setStringValue(_data); - } else if (st.type == "unknown") { - ret = st.node->setUnknownValue(_data); + } else if (st.type == "unspecified") { + ret = st.node->setUnspecifiedValue(_data); } else { - SG_LOG(SG_INPUT, SG_ALERT, "Unknown data type " << st.type - << " assuming 'unknown'"); - ret = st.node->setUnknownValue(_data); + string message = "Unrecognized data type '"; + message += st.type; + message += '\''; + // FIXME: add location information + throw sg_io_exception(message, "SimGear Property Reader"); } if (!ret) SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set " @@ -193,6 +250,11 @@ PropsVisitor::endElement (const char * name) << _data << "\" with type " << st.type); } + // Set the access-mode attributes now, + // once the value has already been + // assigned. + st.node->setAttributes(st.mode); + pop_state(); } @@ -210,14 +272,6 @@ PropsVisitor::warning (const char * message, int line, int column) << message << " at line " << line << ", column " << column); } -void -PropsVisitor::error (const char * message, int line, int column) -{ - SG_LOG(SG_INPUT, SG_ALERT, "readProperties: FATAL: " << - message << " at line " << line << ", column " << column); - _ok = false; -} - //////////////////////////////////////////////////////////////////////// @@ -233,12 +287,14 @@ PropsVisitor::error (const char * message, int line, int column) * @param base A base path for resolving external include references. * @return true if the read succeeded, false otherwise. */ -bool +void readProperties (istream &input, SGPropertyNode * start_node, const string &base) { PropsVisitor visitor(start_node, base); - return readXML(input, visitor) && visitor.isOK(); + readXML(input, visitor, base); + if (visitor.hasException()) + throw visitor.getException(); } @@ -249,18 +305,13 @@ readProperties (istream &input, SGPropertyNode * start_node, * @param start_node The root node for reading properties. * @return true if the read succeeded, false otherwise. */ -bool +void readProperties (const string &file, SGPropertyNode * start_node) { - cerr << "Reading properties from " << file << endl; - ifstream input(file.c_str()); - if (input.good()) { - return readProperties(input, start_node, file); - } else { - SG_LOG(SG_INPUT, SG_ALERT, "Error reading property list from file " - << file); - return false; - } + PropsVisitor visitor(start_node, file); + readXML(file, visitor); + if (visitor.hasException()) + throw visitor.getException(); } @@ -275,25 +326,27 @@ readProperties (const string &file, SGPropertyNode * start_node) * Return the type name. */ static const char * -getTypeName (SGValue::Type type) +getTypeName (SGPropertyNode::Type type) { switch (type) { - case SGValue::UNKNOWN: - return "unknown"; - case SGValue::BOOL: + case SGPropertyNode::UNSPECIFIED: + return "unspecified"; + case SGPropertyNode::BOOL: return "bool"; - case SGValue::INT: + case SGPropertyNode::INT: return "int"; - case SGValue::FLOAT: + case SGPropertyNode::LONG: + return "long"; + case SGPropertyNode::FLOAT: return "float"; - case SGValue::DOUBLE: + case SGPropertyNode::DOUBLE: return "double"; - case SGValue::STRING: + case SGPropertyNode::STRING: return "string"; } // keep the compiler from squawking - return "unknown"; + return "unspecified"; } @@ -330,61 +383,96 @@ doIndent (ostream &output, int indent) } +static void +writeAtts (ostream &output, const SGPropertyNode * node) +{ + int index = node->getIndex(); + + if (index != 0) + output << " n=\"" << index << '"'; + +#if 0 + if (!node->getAttribute(SGPropertyNode::READ)) + output << " read=\"n\""; + + if (!node->getAttribute(SGPropertyNode::WRITE)) + output << " write=\"n\""; + + if (node->getAttribute(SGPropertyNode::ARCHIVE)) + output << " archive=\"y\""; +#endif + +} + + +/** + * Test whether a node is archivable or has archivable descendants. + */ +static bool +isArchivable (const SGPropertyNode * node) +{ + // FIXME: it's inefficient to do this all the time + if (node->getAttribute(SGPropertyNode::ARCHIVE)) + return true; + else { + int nChildren = node->nChildren(); + for (int i = 0; i < nChildren; i++) + if (isArchivable(node->getChild(i))) + return true; + } + return false; +} + + static bool writeNode (ostream &output, const SGPropertyNode * node, int indent) { + // Don't write the node or any of + // its descendants unless it is + // allowed to be archived. + if (!isArchivable(node)) + return true; // Everything's OK, but we won't write. + const string &name = node->getName(); int index = node->getIndex(); int nChildren = node->nChildren(); // If there is a literal value, // write it first. - if (node->hasValue()) { + if (node->hasValue() && node->getAttribute(SGPropertyNode::ARCHIVE)) { doIndent(output, indent); - output << '<' << name << " n=\"" << index; + output << '<' << name; + writeAtts(output, node); if (node->isAlias() && node->getAliasTarget() != 0) { - output << "\" alias=\"" - << node->getAliasTarget()->getPath() << "\"/>" << endl; + output << " alias=\"" << node->getAliasTarget()->getPath() + << "\"/>" << endl; } else { - if (node->getType() != SGValue::UNKNOWN) - output << "\" type=\"" << getTypeName(node->getType()) << '"'; + if (node->getType() != SGPropertyNode::UNSPECIFIED) + output << " type=\"" << getTypeName(node->getType()) << '"'; output << '>'; writeData(output, node->getStringValue()); - output << "' << endl;; + output << "' << endl; } } // If there are children, write them // next. - if (nChildren > 0) { + if (nChildren > 0 || node->isAlias()) { doIndent(output, indent); - output << '<' << name << " n=\"" << index << "\">" << endl;; + output << '<' << name; + writeAtts(output, node); + output << '>' << endl; for (int i = 0; i < nChildren; i++) writeNode(output, node->getChild(i), indent + INDENT_STEP); doIndent(output, indent); output << "' << endl; } - // 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 << " n=\"" << index << "\"/>" << endl; - } - return true; } -/** - * Write a property tree to an output stream in XML format. - * - * @param output The output stream. - * @param start_node The root node to write. - * @return true if the write succeeded, false otherwise. - */ -bool +void writeProperties (ostream &output, const SGPropertyNode * start_node) { int nChildren = start_node->nChildren(); @@ -397,28 +485,17 @@ writeProperties (ostream &output, const SGPropertyNode * start_node) } output << "" << endl; - - return true; } -/** - * Write a property tree to a file in XML format. - * - * @param file The destination file. - * @param start_node The root node to write. - * @return true if the write succeeded, false otherwise. - */ -bool +void writeProperties (const string &file, const SGPropertyNode * start_node) { ofstream output(file.c_str()); if (output.good()) { - return writeProperties(output, start_node); + writeProperties(output, start_node); } else { - SG_LOG(SG_INPUT, SG_ALERT, "Cannot write properties to file " - << file); - return false; + throw sg_io_exception("Cannot open file", sg_location(file)); } } @@ -446,32 +523,38 @@ copyProperties (const SGPropertyNode *in, SGPropertyNode *out) // if any. if (in->hasValue()) { switch (in->getType()) { - case SGValue::BOOL: + case SGPropertyNode::BOOL: if (!out->setBoolValue(in->getBoolValue())) retval = false; break; - case SGValue::INT: + case SGPropertyNode::INT: if (!out->setIntValue(in->getIntValue())) retval = false; break; - case SGValue::FLOAT: + case SGPropertyNode::LONG: + if (!out->setLongValue(in->getLongValue())) + retval = false; + break; + case SGPropertyNode::FLOAT: if (!out->setFloatValue(in->getFloatValue())) retval = false; break; - case SGValue::DOUBLE: + case SGPropertyNode::DOUBLE: if (!out->setDoubleValue(in->getDoubleValue())) retval = false; break; - case SGValue::STRING: + case SGPropertyNode::STRING: if (!out->setStringValue(in->getStringValue())) retval = false; break; - case SGValue::UNKNOWN: - if (!out->setUnknownValue(in->getStringValue())) + case SGPropertyNode::UNSPECIFIED: + if (!out->setUnspecifiedValue(in->getStringValue())) retval = false; break; default: - throw string("Unknown SGValue type"); + string message = "Unknown internal SGPropertyNode type"; + message += in->getType(); + throw sg_error(message, "SimGear Property Reader"); } }