X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Fprops_io.cxx;h=160539ae73b41309c9f6d1fe88b9bdeada65976c;hb=5bab565cfe4c30d6cf08ecaba50af74d5e4f0c98;hp=2a0196b7753592a50dd3afb893b80a0141fe2e6e;hpb=8b13d71fcf20714617ff4864461ecae93c5caec0;p=simgear.git diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx index 2a0196b7..160539ae 100644 --- a/simgear/misc/props_io.cxx +++ b/simgear/misc/props_io.cxx @@ -1,26 +1,36 @@ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include // atof() atoi() +#include #include #include +#include "sg_path.hxx" #include "props.hxx" -#include -#include -#include +#include STL_IOSTREAM +#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS) +# include +#else +# include +#endif +#include STL_STRING #include +#include + +#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS) +SG_USING_STD(istream); +SG_USING_STD(ifstream); +SG_USING_STD(ostream); +SG_USING_STD(ofstream); +#endif +SG_USING_STD(string); +SG_USING_STD(vector); +SG_USING_STD(map); -using std::istream; -using std::ifstream; -using std::ostream; -using std::ofstream; -using std::string; -using std::vector; +#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE) @@ -32,7 +42,10 @@ class PropsVisitor : public XMLVisitor { public: - PropsVisitor (SGPropertyNode * root) : _ok(true), _root(root), _level(0) {} + PropsVisitor (SGPropertyNode * root, const string &base) + : _root(root), _level(0), _base(base), _hasException(false) {} + + virtual ~PropsVisitor () {} void startXML (); void endXML (); @@ -40,28 +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 = ""; } @@ -71,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 @@ -93,20 +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) { - push_state(_root, ""); + 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, "", DEFAULT_MODE); } else { - const char * att_n = atts.getValue("n"); + + const char * attval; + // Get the index. + attval = 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")); + if (attval != 0) { + index = atoi(attval); + st.counters[name] = SG_MAX2(st.counters[name], index+1); + } else { + index = st.counters[name]; + st.counters[name]++; + } + + // Got the index, so grab the node. + SGPropertyNode * node = st.node->getChild(name, index, true); + + // 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. + 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"), mode); } } @@ -116,9 +217,9 @@ 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 there are no children and it's + // not an alias, then it's a leaf value. + if (st.node->nChildren() == 0 && !st.node->isAlias()) { if (st.type == "bool") { if (_data == "true" || atoi(_data.c_str()) != 0) ret = st.node->setBoolValue(true); @@ -126,25 +227,33 @@ 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 { - FG_LOG(FG_INPUT, FG_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 " + << st.node->getPath() << " to value \"" + << _data << "\" with type " << st.type); } - if (!ret) - FG_LOG(FG_INPUT, FG_ALERT, "readProperties: Failed to set " - << st.node->getPath() << " to value \"" - << _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(); } @@ -159,42 +268,50 @@ PropsVisitor::data (const char * s, int length) void PropsVisitor::warning (const char * message, int line, int column) { - FG_LOG(FG_INPUT, FG_ALERT, "readProperties: warning: " + SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: " << message << " at line " << line << ", column " << column); } -void -PropsVisitor::error (const char * message, int line, int column) -{ - FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: " - << message << " at line " << line << ", column " << column); - _ok = false; -} - //////////////////////////////////////////////////////////////////////// // Property list reader. //////////////////////////////////////////////////////////////////////// -bool -readProperties (istream &input, SGPropertyNode * start_node) + +/** + * Read properties from an input stream. + * + * @param input The input stream containing an XML property file. + * @param start_node The root node for reading properties. + * @param base A base path for resolving external include references. + * @return true if the read succeeded, false otherwise. + */ +void +readProperties (istream &input, SGPropertyNode * start_node, + const string &base) { - PropsVisitor visitor(start_node); - return readXML(input, visitor) && visitor.isOK(); + PropsVisitor visitor(start_node, base); + readXML(input, visitor, base); + if (visitor.hasException()) + throw visitor.getException(); } -bool + +/** + * Read properties from a file. + * + * @param file A string containing the file path. + * @param start_node The root node for reading properties. + * @return true if the read succeeded, false otherwise. + */ +void readProperties (const string &file, SGPropertyNode * start_node) { - ifstream input(file.c_str()); - if (input.good()) { - return readProperties(input, start_node); - } else { - FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file " - << file); - return false; - } + PropsVisitor visitor(start_node, file); + readXML(file, visitor); + if (visitor.hasException()) + throw visitor.getException(); } @@ -209,22 +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 "unspecified"; } @@ -234,7 +356,7 @@ getTypeName (SGValue::Type type) static void writeData (ostream &output, const string &data) { - for (int i = 0; i < data.size(); i++) { + for (int i = 0; i < (int)data.size(); i++) { switch (data[i]) { case '&': output << "&"; @@ -261,46 +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 - << "\" type=\"" << getTypeName(node->getType()) << "\">"; - writeData(output, node->getStringValue()); - output << "' << endl;; + output << '<' << name; + writeAtts(output, node); + if (node->isAlias() && node->getAliasTarget() != 0) { + output << " alias=\"" << node->getAliasTarget()->getPath() + << "\"/>" << endl; + } else { + if (node->getType() != SGPropertyNode::UNSPECIFIED) + output << " type=\"" << getTypeName(node->getType()) << '"'; + output << '>'; + writeData(output, node->getStringValue()); + 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; } -bool + +void writeProperties (ostream &output, const SGPropertyNode * start_node) { int nChildren = start_node->nChildren(); @@ -313,19 +485,91 @@ writeProperties (ostream &output, const SGPropertyNode * start_node) } output << "" << endl; - - return true; } -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 { - FG_LOG(FG_INPUT, FG_ALERT, "Cannot write properties to file " - << file); - return false; + throw sg_io_exception("Cannot open file", sg_location(file)); } } + + + +//////////////////////////////////////////////////////////////////////// +// Copy properties from one tree to another. +//////////////////////////////////////////////////////////////////////// + + +/** + * Copy one property tree to another. + * + * @param in The source property tree. + * @param out The destination property tree. + * @return true if all properties were copied, false if some failed + * (for example, if the property's value is tied read-only). + */ +bool +copyProperties (const SGPropertyNode *in, SGPropertyNode *out) +{ + bool retval = true; + + // First, copy the actual value, + // if any. + if (in->hasValue()) { + switch (in->getType()) { + case SGPropertyNode::BOOL: + if (!out->setBoolValue(in->getBoolValue())) + retval = false; + break; + case SGPropertyNode::INT: + if (!out->setIntValue(in->getIntValue())) + retval = false; + break; + case SGPropertyNode::LONG: + if (!out->setLongValue(in->getLongValue())) + retval = false; + break; + case SGPropertyNode::FLOAT: + if (!out->setFloatValue(in->getFloatValue())) + retval = false; + break; + case SGPropertyNode::DOUBLE: + if (!out->setDoubleValue(in->getDoubleValue())) + retval = false; + break; + case SGPropertyNode::STRING: + if (!out->setStringValue(in->getStringValue())) + retval = false; + break; + case SGPropertyNode::UNSPECIFIED: + if (!out->setUnspecifiedValue(in->getStringValue())) + retval = false; + break; + default: + string message = "Unknown internal SGPropertyNode type"; + message += in->getType(); + throw sg_error(message, "SimGear Property Reader"); + } + } + + // Next, copy the children. + int nChildren = in->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode * in_child = in->getChild(i); + SGPropertyNode * out_child = out->getChild(in_child->getName(), + in_child->getIndex(), + true); + if (!copyProperties(in_child, out_child)) + retval = false; + } + + return retval; +} + +// end of props_io.cxx