00001
00011 #ifdef HAVE_CONFIG_H
00012 # include <simgear_config.h>
00013 #endif
00014
00015 #include <simgear/compiler.h>
00016
00017 #include <stdlib.h>
00018
00019 #include <simgear/sg_inlines.h>
00020 #include <simgear/debug/logstream.hxx>
00021 #include <simgear/math/SGMath.hxx>
00022 #include <simgear/misc/sg_path.hxx>
00023 #include <simgear/xml/easyxml.hxx>
00024
00025 #include "props.hxx"
00026 #include "props_io.hxx"
00027
00028 #include <iostream>
00029 #include <fstream>
00030 #include <string>
00031 #include <cstring>
00032 #include <vector>
00033 #include <map>
00034
00035 using std::istream;
00036 using std::ifstream;
00037 using std::ostream;
00038 using std::ofstream;
00039 using std::string;
00040 using std::vector;
00041 using std::map;
00042
00043 using std::endl;
00044
00045 #define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
00046
00047
00048
00050
00052
00053 class PropsVisitor : public XMLVisitor
00054 {
00055 public:
00056
00057 PropsVisitor (SGPropertyNode * root, const string &base, int default_mode = 0,
00058 bool extended = false)
00059 : _default_mode(default_mode), _root(root), _level(0), _base(base),
00060 _hasException(false), _extended(extended)
00061 {}
00062
00063 virtual ~PropsVisitor () {}
00064
00065 void startXML ();
00066 void endXML ();
00067 void startElement (const char * name, const XMLAttributes &atts);
00068 void endElement (const char * name);
00069 void data (const char * s, int length);
00070 void warning (const char * message, int line, int column);
00071
00072 bool hasException () const { return _hasException; }
00073 sg_io_exception &getException () { return _exception; }
00074 void setException (const sg_io_exception &exception) {
00075 _exception = exception;
00076 _hasException = true;
00077 }
00078
00079 private:
00080
00081 struct State
00082 {
00083 State () : node(0), type(""), mode(DEFAULT_MODE), omit(false) {}
00084 State (SGPropertyNode * _node, const char * _type, int _mode, bool _omit)
00085 : node(_node), type(_type), mode(_mode), omit(_omit) {}
00086 SGPropertyNode * node;
00087 string type;
00088 int mode;
00089 bool omit;
00090 map<string,int> counters;
00091 };
00092
00093 State &state () { return _state_stack[_state_stack.size() - 1]; }
00094
00095 void push_state (SGPropertyNode * node, const char * type, int mode, bool omit = false) {
00096 if (type == 0)
00097 _state_stack.push_back(State(node, "unspecified", mode, omit));
00098 else
00099 _state_stack.push_back(State(node, type, mode, omit));
00100 _level++;
00101 _data = "";
00102 }
00103
00104 void pop_state () {
00105 _state_stack.pop_back();
00106 _level--;
00107 }
00108
00109 int _default_mode;
00110 string _data;
00111 SGPropertyNode * _root;
00112 SGPropertyNode null;
00113 int _level;
00114 vector<State> _state_stack;
00115 string _base;
00116 sg_io_exception _exception;
00117 bool _hasException;
00118 bool _extended;
00119 };
00120
00121 void
00122 PropsVisitor::startXML ()
00123 {
00124 _level = 0;
00125 _state_stack.resize(0);
00126 }
00127
00128 void
00129 PropsVisitor::endXML ()
00130 {
00131 _level = 0;
00132 _state_stack.resize(0);
00133 }
00134
00135
00139 static bool
00140 checkFlag (const char * flag, bool defaultState = true)
00141 {
00142 if (flag == 0)
00143 return defaultState;
00144 else if (!strcmp(flag, "y"))
00145 return true;
00146 else if (!strcmp(flag, "n"))
00147 return false;
00148 else {
00149 string message = "Unrecognized flag value '";
00150 message += flag;
00151 message += '\'';
00152
00153 throw sg_io_exception(message, "SimGear Property Reader");
00154 }
00155 }
00156
00157 void
00158 PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
00159 {
00160 const char * attval;
00161
00162 if (_level == 0) {
00163 if (strcmp(name, "PropertyList")) {
00164 string message = "Root element name is ";
00165 message += name;
00166 message += "; expected PropertyList";
00167 throw sg_io_exception(message, "SimGear Property Reader");
00168 }
00169
00170
00171 attval = atts.getValue("include");
00172 if (attval != 0) {
00173 SGPath path(SGPath(_base).dir());
00174 path.append(attval);
00175 try {
00176 readProperties(path.str(), _root, 0, _extended);
00177 } catch (sg_io_exception &e) {
00178 setException(e);
00179 }
00180 }
00181
00182 push_state(_root, "", DEFAULT_MODE);
00183 }
00184
00185 else {
00186 State &st = state();
00187
00188 attval = atts.getValue("n");
00189 int index = 0;
00190 string strName(name);
00191 if (attval != 0) {
00192 index = atoi(attval);
00193 st.counters[strName] = SG_MAX2(st.counters[strName], index+1);
00194 } else {
00195 index = st.counters[strName];
00196 st.counters[strName]++;
00197 }
00198
00199
00200 SGPropertyNode * node = st.node->getChild(strName, index, true);
00201 if (!node->getAttribute(SGPropertyNode::WRITE)) {
00202 SG_LOG(SG_INPUT, SG_ALERT, "Not overwriting write-protected property "
00203 << node->getPath(true));
00204 node = &null;
00205 }
00206
00207
00208
00209
00210 int mode = _default_mode;
00211
00212 attval = atts.getValue("read");
00213 if (checkFlag(attval, true))
00214 mode |= SGPropertyNode::READ;
00215 attval = atts.getValue("write");
00216 if (checkFlag(attval, true))
00217 mode |= SGPropertyNode::WRITE;
00218 attval = atts.getValue("archive");
00219 if (checkFlag(attval, false))
00220 mode |= SGPropertyNode::ARCHIVE;
00221 attval = atts.getValue("trace-read");
00222 if (checkFlag(attval, false))
00223 mode |= SGPropertyNode::TRACE_READ;
00224 attval = atts.getValue("trace-write");
00225 if (checkFlag(attval, false))
00226 mode |= SGPropertyNode::TRACE_WRITE;
00227 attval = atts.getValue("userarchive");
00228 if (checkFlag(attval, false))
00229 mode |= SGPropertyNode::USERARCHIVE;
00230
00231
00232 attval = atts.getValue("alias");
00233 if (attval != 0) {
00234 if (!node->alias(attval))
00235 SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
00236 }
00237
00238
00239 bool omit = false;
00240 attval = atts.getValue("include");
00241 if (attval != 0) {
00242 SGPath path(SGPath(_base).dir());
00243 path.append(attval);
00244 try {
00245 readProperties(path.str(), node, 0, _extended);
00246 } catch (sg_io_exception &e) {
00247 setException(e);
00248 }
00249
00250 attval = atts.getValue("omit-node");
00251 omit = checkFlag(attval, false);
00252 }
00253
00254 const char *type = atts.getValue("type");
00255 if (type)
00256 node->clearValue();
00257 push_state(node, type, mode, omit);
00258 }
00259 }
00260
00261 void
00262 PropsVisitor::endElement (const char * name)
00263 {
00264 State &st = state();
00265 bool ret;
00266
00267
00268
00269 if (st.node->nChildren() == 0 && !st.node->isAlias()) {
00270 if (st.type == "bool") {
00271 if (_data == "true" || atoi(_data.c_str()) != 0)
00272 ret = st.node->setBoolValue(true);
00273 else
00274 ret = st.node->setBoolValue(false);
00275 } else if (st.type == "int") {
00276 ret = st.node->setIntValue(atoi(_data.c_str()));
00277 } else if (st.type == "long") {
00278 ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
00279 } else if (st.type == "float") {
00280 ret = st.node->setFloatValue(atof(_data.c_str()));
00281 } else if (st.type == "double") {
00282 ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
00283 } else if (st.type == "string") {
00284 ret = st.node->setStringValue(_data.c_str());
00285 } else if (st.type == "vec3d" && _extended) {
00286 ret = st.node
00287 ->setValue(simgear::parseString<SGVec3d>(_data));
00288 } else if (st.type == "vec4d" && _extended) {
00289 ret = st.node
00290 ->setValue(simgear::parseString<SGVec4d>(_data));
00291 } else if (st.type == "unspecified") {
00292 ret = st.node->setUnspecifiedValue(_data.c_str());
00293 } else if (_level == 1) {
00294 ret = true;
00295 } else {
00296 string message = "Unrecognized data type '";
00297 message += st.type;
00298 message += '\'';
00299
00300 throw sg_io_exception(message, "SimGear Property Reader");
00301 }
00302 if (!ret)
00303 SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
00304 << st.node->getPath() << " to value \""
00305 << _data << "\" with type " << st.type);
00306 }
00307
00308
00309
00310
00311 st.node->setAttributes(st.mode);
00312
00313 if (st.omit) {
00314 State &parent = _state_stack[_state_stack.size() - 2];
00315 int nChildren = st.node->nChildren();
00316 for (int i = 0; i < nChildren; i++) {
00317 SGPropertyNode *src = st.node->getChild(i);
00318 const char *name = src->getName();
00319 int index = parent.counters[name];
00320 parent.counters[name]++;
00321 SGPropertyNode *dst = parent.node->getChild(name, index, true);
00322 copyProperties(src, dst);
00323 }
00324 parent.node->removeChild(st.node->getName(), st.node->getIndex(), false);
00325 }
00326 pop_state();
00327 }
00328
00329 void
00330 PropsVisitor::data (const char * s, int length)
00331 {
00332 if (state().node->nChildren() == 0)
00333 _data.append(string(s, length));
00334 }
00335
00336 void
00337 PropsVisitor::warning (const char * message, int line, int column)
00338 {
00339 SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
00340 << message << " at line " << line << ", column " << column);
00341 }
00342
00343
00344
00346
00348
00349
00358 void
00359 readProperties (istream &input, SGPropertyNode * start_node,
00360 const string &base, int default_mode, bool extended)
00361 {
00362 PropsVisitor visitor(start_node, base, default_mode, extended);
00363 readXML(input, visitor, base);
00364 if (visitor.hasException())
00365 throw visitor.getException();
00366 }
00367
00368
00376 void
00377 readProperties (const string &file, SGPropertyNode * start_node,
00378 int default_mode, bool extended)
00379 {
00380 PropsVisitor visitor(start_node, file, default_mode, extended);
00381 readXML(file, visitor);
00382 if (visitor.hasException())
00383 throw visitor.getException();
00384 }
00385
00386
00395 void readProperties (const char *buf, const int size,
00396 SGPropertyNode * start_node, int default_mode,
00397 bool extended)
00398 {
00399 PropsVisitor visitor(start_node, "", default_mode, extended);
00400 readXML(buf, size, visitor);
00401 if (visitor.hasException())
00402 throw visitor.getException();
00403 }
00404
00405
00407
00409
00410 #define INDENT_STEP 2
00411
00415 static const char *
00416 getTypeName (simgear::props::Type type)
00417 {
00418 using namespace simgear;
00419 switch (type) {
00420 case props::UNSPECIFIED:
00421 return "unspecified";
00422 case props::BOOL:
00423 return "bool";
00424 case props::INT:
00425 return "int";
00426 case props::LONG:
00427 return "long";
00428 case props::FLOAT:
00429 return "float";
00430 case props::DOUBLE:
00431 return "double";
00432 case props::STRING:
00433 return "string";
00434 case props::VEC3D:
00435 return "vec3d";
00436 case props::VEC4D:
00437 return "vec4d";
00438 case props::ALIAS:
00439 case props::NONE:
00440 return "unspecified";
00441 default:
00442 break;
00443 }
00444
00445
00446 return "unspecified";
00447 }
00448
00449
00453 static void
00454 writeData (ostream &output, const string &data)
00455 {
00456 for (int i = 0; i < (int)data.size(); i++) {
00457 switch (data[i]) {
00458 case '&':
00459 output << "&";
00460 break;
00461 case '<':
00462 output << "<";
00463 break;
00464 case '>':
00465 output << ">";
00466 break;
00467 default:
00468 output << data[i];
00469 break;
00470 }
00471 }
00472 }
00473
00474 static void
00475 doIndent (ostream &output, int indent)
00476 {
00477 while (indent-- > 0) {
00478 output << ' ';
00479 }
00480 }
00481
00482
00483 static void
00484 writeAtts (ostream &output, const SGPropertyNode * node, bool forceindex)
00485 {
00486 int index = node->getIndex();
00487
00488 if (index != 0 || forceindex)
00489 output << " n=\"" << index << '"';
00490
00491 #if 0
00492 if (!node->getAttribute(SGPropertyNode::READ))
00493 output << " read=\"n\"";
00494
00495 if (!node->getAttribute(SGPropertyNode::WRITE))
00496 output << " write=\"n\"";
00497
00498 if (node->getAttribute(SGPropertyNode::ARCHIVE))
00499 output << " archive=\"y\"";
00500 #endif
00501
00502 }
00503
00504
00508 static bool
00509 isArchivable (const SGPropertyNode * node, SGPropertyNode::Attribute archive_flag)
00510 {
00511
00512 if (node->getAttribute(archive_flag))
00513 return true;
00514 else {
00515 int nChildren = node->nChildren();
00516 for (int i = 0; i < nChildren; i++)
00517 if (isArchivable(node->getChild(i), archive_flag))
00518 return true;
00519 }
00520 return false;
00521 }
00522
00523
00524 static bool
00525 writeNode (ostream &output, const SGPropertyNode * node,
00526 bool write_all, int indent, SGPropertyNode::Attribute archive_flag)
00527 {
00528
00529
00530
00531 if (!write_all && !isArchivable(node, archive_flag))
00532 return true;
00533
00534 const string name = node->getName();
00535 int nChildren = node->nChildren();
00536 bool node_has_value = false;
00537
00538
00539
00540 if (node->hasValue() && (write_all || node->getAttribute(archive_flag))) {
00541 doIndent(output, indent);
00542 output << '<' << name;
00543 writeAtts(output, node, nChildren != 0);
00544 if (node->isAlias() && node->getAliasTarget() != 0) {
00545 output << " alias=\"" << node->getAliasTarget()->getPath()
00546 << "\"/>" << endl;
00547 } else {
00548 if (node->getType() != simgear::props::UNSPECIFIED)
00549 output << " type=\"" << getTypeName(node->getType()) << '"';
00550 output << '>';
00551 writeData(output, node->getStringValue());
00552 output << "</" << name << '>' << endl;
00553 }
00554 node_has_value = true;
00555 }
00556
00557 // If there are children, write them next.
00558 if (nChildren > 0) {
00559 doIndent(output, indent);
00560 output << '<' << name;
00561 writeAtts(output, node, node_has_value);
00562 output << '>' << endl;
00563 for (int i = 0; i < nChildren; i++)
00564 writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP, archive_flag);
00565 doIndent(output, indent);
00566 output << "</" << name << '>' << endl;
00567 }
00568
00569 return true;
00570 }
00571
00572
00573 void
00574 writeProperties (ostream &output, const SGPropertyNode * start_node,
00575 bool write_all, SGPropertyNode::Attribute archive_flag)
00576 {
00577 int nChildren = start_node->nChildren();
00578
00579 output << "<?xml version=\"1.0\"?>" << endl << endl;
00580 output << "<PropertyList>" << endl;
00581
00582 for (int i = 0; i < nChildren; i++) {
00583 writeNode(output, start_node->getChild(i), write_all, INDENT_STEP, archive_flag);
00584 }
00585
00586 output << "</PropertyList>" << endl;
00587 }
00588
00589
00590 void
00591 writeProperties (const string &file, const SGPropertyNode * start_node,
00592 bool write_all, SGPropertyNode::Attribute archive_flag)
00593 {
00594 SGPath path(file.c_str());
00595 path.create_dir(0777);
00596
00597 ofstream output(file.c_str());
00598 if (output.good()) {
00599 writeProperties(output, start_node, write_all, archive_flag);
00600 } else {
00601 throw sg_io_exception("Cannot open file", sg_location(file));
00602 }
00603 }
00604
00605 // Another variation, useful when called from gdb
00606 void
00607 writeProperties (const char* file, const SGPropertyNode * start_node)
00608 {
00609 writeProperties(string(file), start_node, true);
00610 }
00611
00612
00613
00615 // Copy properties from one tree to another.
00617
00618
00627 bool
00628 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
00629 {
00630 using namespace simgear;
00631 bool retval = true;
00632
00633 // First, copy the actual value,
00634 // if any.
00635 if (in->hasValue()) {
00636 switch (in->getType()) {
00637 case props::BOOL:
00638 if (!out->setBoolValue(in->getBoolValue()))
00639 retval = false;
00640 break;
00641 case props::INT:
00642 if (!out->setIntValue(in->getIntValue()))
00643 retval = false;
00644 break;
00645 case props::LONG:
00646 if (!out->setLongValue(in->getLongValue()))
00647 retval = false;
00648 break;
00649 case props::FLOAT:
00650 if (!out->setFloatValue(in->getFloatValue()))
00651 retval = false;
00652 break;
00653 case props::DOUBLE:
00654 if (!out->setDoubleValue(in->getDoubleValue()))
00655 retval = false;
00656 break;
00657 case props::STRING:
00658 if (!out->setStringValue(in->getStringValue()))
00659 retval = false;
00660 break;
00661 case props::UNSPECIFIED:
00662 if (!out->setUnspecifiedValue(in->getStringValue()))
00663 retval = false;
00664 break;
00665 case props::VEC3D:
00666 if (!out->setValue(in->getValue<SGVec3d>()))
00667 retval = false;
00668 break;
00669 case props::VEC4D:
00670 if (!out->setValue(in->getValue<SGVec4d>()))
00671 retval = false;
00672 break;
00673 default:
00674 if (in->isAlias())
00675 break;
00676 string message = "Unknown internal SGPropertyNode type";
00677 message += in->getType();
00678 throw sg_error(message, "SimGear Property Reader");
00679 }
00680 }
00681
00682 // copy the attributes.
00683 out->setAttributes( in->getAttributes() );
00684
00685 // Next, copy the children.
00686 int nChildren = in->nChildren();
00687 for (int i = 0; i < nChildren; i++) {
00688 const SGPropertyNode * in_child = in->getChild(i);
00689 SGPropertyNode * out_child = out->getChild(in_child->getNameString(),
00690 in_child->getIndex(),
00691 true);
00692 if (!copyProperties(in_child, out_child))
00693 retval = false;
00694 }
00695
00696 return retval;
00697 }
00698
00699 // end of props_io.cxx