X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Fprops_io.cxx;h=e4b8e8009e5d762215bdc894f2d8582e944fb2f6;hb=f1a79e6b9fb70208f19bd9c19f68617addcca2e0;hp=b1294937501112e7e9e83942e11153d0d524a34d;hpb=e07af680183126847f1547a7ad8101e43fbe9023;p=simgear.git diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx index b1294937..e4b8e800 100644 --- a/simgear/misc/props_io.cxx +++ b/simgear/misc/props_io.cxx @@ -1,34 +1,36 @@ -#ifdef HAVE_CONFIG_H -# include -#endif - #include #include // atof() atoi() +#include #include #include +#include "sg_path.hxx" #include "props.hxx" #include STL_IOSTREAM -#if !defined(FG_HAVE_NATIVE_SGI_COMPILERS) +#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS) # include #else # include #endif #include STL_STRING #include +#include -#if !defined(FG_HAVE_NATIVE_SGI_COMPILERS) -FG_USING_STD(istream); -FG_USING_STD(ifstream); -FG_USING_STD(ostream); -FG_USING_STD(ofstream); +#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS) +SG_USING_STD(istream); +SG_USING_STD(ifstream); +SG_USING_STD(ostream); +SG_USING_STD(ofstream); #endif -FG_USING_STD(string); -FG_USING_STD(vector); +SG_USING_STD(string); +SG_USING_STD(vector); +SG_USING_STD(map); + +#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE) @@ -40,7 +42,8 @@ class PropsVisitor : public XMLVisitor { public: - PropsVisitor (SGPropertyNode * root) : _ok(true), _root(root), _level(0) {} + PropsVisitor (SGPropertyNode * root, const string &base) + : _ok(true), _root(root), _level(0), _base(base) {} void startXML (); void endXML (); @@ -56,20 +59,22 @@ 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,7 +89,7 @@ private: SGPropertyNode * _root; int _level; vector _state_stack; - + string _base; }; void @@ -101,20 +106,94 @@ 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 { + SG_LOG(SG_INPUT, SG_ALERT, "Unrecognized flag value '" << flag + << "', assuming yes"); + return true; + } +} + void PropsVisitor::startElement (const char * name, const XMLAttributes &atts) { + State &st = state(); + if (_level == 0) { - push_state(_root, ""); + if (string(name) != (string)"PropertyList") { + SG_LOG(SG_INPUT, SG_ALERT, "Root element name is " << + name << "; expected PropertyList"); + _ok = false; + } + 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; + + // 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()); + cerr << "Base is " << _base << endl; + cerr << "Dir is " << SGPath(_base).dir() << endl; + path.append(attval); + if (!readProperties(path.str(), node)) { + SG_LOG(SG_INPUT, SG_ALERT, "Failed to read include file " + << attval); + _ok = false; + } + } + + push_state(node, atts.getValue("type"), mode); } } @@ -124,9 +203,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); @@ -134,25 +213,32 @@ 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); + SG_LOG(SG_INPUT, SG_ALERT, "Unrecognized data type " << st.type + << " assuming 'unspecified'"); + ret = st.node->setUnspecifiedValue(_data); } if (!ret) - FG_LOG(FG_INPUT, FG_ALERT, "readProperties: Failed to set " + SG_LOG(SG_INPUT, SG_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(); } @@ -166,15 +252,15 @@ 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); + SG_LOG(SG_INPUT, SG_ALERT, "readProperties: FATAL: " << + message << " at line " << line << ", column " << column); _ok = false; } @@ -184,21 +270,40 @@ PropsVisitor::error (const char * message, int line, int column) // Property list reader. //////////////////////////////////////////////////////////////////////// + +/** + * 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. + */ bool -readProperties (istream &input, SGPropertyNode * start_node) +readProperties (istream &input, SGPropertyNode * start_node, + const string &base) { - PropsVisitor visitor(start_node); + PropsVisitor visitor(start_node, base); return readXML(input, visitor) && visitor.isOK(); } + +/** + * 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. + */ bool 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); + return readProperties(input, start_node, file); } else { - FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file " + SG_LOG(SG_INPUT, SG_ALERT, "Error reading property list from file " << file); return false; } @@ -216,22 +321,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"; } @@ -241,7 +351,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 << "&"; @@ -268,45 +378,102 @@ 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; } + +/** + * 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 writeProperties (ostream &output, const SGPropertyNode * start_node) { @@ -324,6 +491,14 @@ writeProperties (ostream &output, const SGPropertyNode * start_node) 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 writeProperties (const string &file, const SGPropertyNode * start_node) { @@ -331,15 +506,26 @@ writeProperties (const string &file, const SGPropertyNode * start_node) if (output.good()) { return writeProperties(output, start_node); } else { - FG_LOG(FG_INPUT, FG_ALERT, "Cannot write properties to file " + SG_LOG(SG_INPUT, SG_ALERT, "Cannot write properties to file " << file); return false; } } + +//////////////////////////////////////////////////////////////////////// +// Copy properties from one tree to another. +//////////////////////////////////////////////////////////////////////// + + /** - * Copy one property list 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) @@ -350,32 +536,36 @@ 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"); // FIXME!!! + throw string("Unrecognized SGPropertyNode type"); } }