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/misc/sg_path.hxx>
00022 #include <simgear/xml/easyxml.hxx>
00023
00024 #include "props.hxx"
00025 #include "props_io.hxx"
00026
00027 #include STL_IOSTREAM
00028 #include STL_FSTREAM
00029 #include STL_STRING
00030 #include <vector>
00031 #include <map>
00032
00033 SG_USING_STD(istream);
00034 SG_USING_STD(ifstream);
00035 SG_USING_STD(ostream);
00036 SG_USING_STD(ofstream);
00037 SG_USING_STD(string);
00038 SG_USING_STD(vector);
00039 SG_USING_STD(map);
00040
00041 #define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
00042
00043
00044
00046
00048
00049 class PropsVisitor : public XMLVisitor
00050 {
00051 public:
00052
00053 PropsVisitor (SGPropertyNode * root, const string &base, int default_mode = 0)
00054 : _default_mode(default_mode), _root(root), _level(0), _base(base), _hasException(false) {}
00055
00056 virtual ~PropsVisitor () {}
00057
00058 void startXML ();
00059 void endXML ();
00060 void startElement (const char * name, const XMLAttributes &atts);
00061 void endElement (const char * name);
00062 void data (const char * s, int length);
00063 void warning (const char * message, int line, int column);
00064
00065 bool hasException () const { return _hasException; }
00066 sg_io_exception &getException () { return _exception; }
00067 void setException (const sg_io_exception &exception) {
00068 _exception = exception;
00069 _hasException = true;
00070 }
00071
00072 private:
00073
00074 struct State
00075 {
00076 State () : node(0), type(""), mode(DEFAULT_MODE) {}
00077 State (SGPropertyNode * _node, const char * _type, int _mode)
00078 : node(_node), type(_type), mode(_mode) {}
00079 SGPropertyNode * node;
00080 string type;
00081 int mode;
00082 map<string,int> counters;
00083 };
00084
00085 State &state () { return _state_stack[_state_stack.size() - 1]; }
00086
00087 void push_state (SGPropertyNode * node, const char * type, int mode) {
00088 if (type == 0)
00089 _state_stack.push_back(State(node, "unspecified", mode));
00090 else
00091 _state_stack.push_back(State(node, type, mode));
00092 _level++;
00093 _data = "";
00094 }
00095
00096 void pop_state () {
00097 _state_stack.pop_back();
00098 _level--;
00099 }
00100
00101 int _default_mode;
00102 string _data;
00103 SGPropertyNode * _root;
00104 int _level;
00105 vector<State> _state_stack;
00106 string _base;
00107 sg_io_exception _exception;
00108 bool _hasException;
00109 };
00110
00111 void
00112 PropsVisitor::startXML ()
00113 {
00114 _level = 0;
00115 _state_stack.resize(0);
00116 }
00117
00118 void
00119 PropsVisitor::endXML ()
00120 {
00121 _level = 0;
00122 _state_stack.resize(0);
00123 }
00124
00125
00129 static bool
00130 checkFlag (const char * flag, bool defaultState = true)
00131 {
00132 if (flag == 0)
00133 return defaultState;
00134 else if (!strcmp(flag, "y"))
00135 return true;
00136 else if (!strcmp(flag, "n"))
00137 return false;
00138 else {
00139 string message = "Unrecognized flag value '";
00140 message += flag;
00141 message += '\'';
00142
00143 throw sg_io_exception(message, "SimGear Property Reader");
00144 }
00145 }
00146
00147 void
00148 PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
00149 {
00150 const char * attval;
00151
00152 if (_level == 0) {
00153 if (strcmp(name, "PropertyList")) {
00154 string message = "Root element name is ";
00155 message += name;
00156 message += "; expected PropertyList";
00157 throw sg_io_exception(message, "SimGear Property Reader");
00158 }
00159
00160
00161 attval = atts.getValue("include");
00162 if (attval != 0) {
00163 SGPath path(SGPath(_base).dir());
00164 path.append(attval);
00165 try {
00166 readProperties(path.str(), _root);
00167 } catch (sg_io_exception &e) {
00168 setException(e);
00169 }
00170 }
00171
00172 push_state(_root, "", DEFAULT_MODE);
00173 }
00174
00175 else {
00176 State &st = state();
00177
00178 attval = atts.getValue("n");
00179 int index = 0;
00180 if (attval != 0) {
00181 index = atoi(attval);
00182 st.counters[name] = SG_MAX2(st.counters[name], index+1);
00183 } else {
00184 index = st.counters[name];
00185 st.counters[name]++;
00186 }
00187
00188
00189 SGPropertyNode * node = st.node->getChild(name, index, true);
00190
00191
00192
00193
00194 int mode = _default_mode;
00195
00196 attval = atts.getValue("read");
00197 if (checkFlag(attval, true))
00198 mode |= SGPropertyNode::READ;
00199 attval = atts.getValue("write");
00200 if (checkFlag(attval, true))
00201 mode |= SGPropertyNode::WRITE;
00202 attval = atts.getValue("archive");
00203 if (checkFlag(attval, false))
00204 mode |= SGPropertyNode::ARCHIVE;
00205 attval = atts.getValue("trace-read");
00206 if (checkFlag(attval, false))
00207 mode |= SGPropertyNode::TRACE_READ;
00208 attval = atts.getValue("trace-write");
00209 if (checkFlag(attval, false))
00210 mode |= SGPropertyNode::TRACE_WRITE;
00211 attval = atts.getValue("userarchive");
00212 if (checkFlag(attval, false))
00213 mode |= SGPropertyNode::USERARCHIVE;
00214
00215
00216 attval = atts.getValue("alias");
00217 if (attval != 0) {
00218 if (!node->alias(attval))
00219 SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
00220 }
00221
00222
00223 attval = atts.getValue("include");
00224 if (attval != 0) {
00225 SGPath path(SGPath(_base).dir());
00226 path.append(attval);
00227 try {
00228 readProperties(path.str(), node);
00229 } catch (sg_io_exception &e) {
00230 setException(e);
00231 }
00232
00233 const char *omit = atts.getValue("omit-node");
00234 if (omit && !strcmp(omit, "y")) {
00235 int nChildren = node->nChildren();
00236 for (int i = 0; i < nChildren; i++) {
00237 SGPropertyNode *src = node->getChild(i);
00238 const char *name = src->getName();
00239 int index = st.counters[name];
00240 st.counters[name]++;
00241 SGPropertyNode *dst = st.node->getChild(name, index, true);
00242 copyProperties(src, dst);
00243 }
00244 st.node->removeChild(node->getName(), node->getIndex(), false);
00245 node = st.node;
00246 }
00247 }
00248
00249 const char *type = atts.getValue("type");
00250 if (type)
00251 node->clearValue();
00252 push_state(node, type, mode);
00253 }
00254 }
00255
00256 void
00257 PropsVisitor::endElement (const char * name)
00258 {
00259 State &st = state();
00260 bool ret;
00261
00262
00263
00264 if (st.node->nChildren() == 0 && !st.node->isAlias()) {
00265 if (st.type == "bool") {
00266 if (_data == "true" || atoi(_data.c_str()) != 0)
00267 ret = st.node->setBoolValue(true);
00268 else
00269 ret = st.node->setBoolValue(false);
00270 } else if (st.type == "int") {
00271 ret = st.node->setIntValue(atoi(_data.c_str()));
00272 } else if (st.type == "long") {
00273 ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
00274 } else if (st.type == "float") {
00275 ret = st.node->setFloatValue(atof(_data.c_str()));
00276 } else if (st.type == "double") {
00277 ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
00278 } else if (st.type == "string") {
00279 ret = st.node->setStringValue(_data.c_str());
00280 } else if (st.type == "unspecified") {
00281 ret = st.node->setUnspecifiedValue(_data.c_str());
00282 } else if (_level == 1) {
00283 ret = true;
00284 } else {
00285 string message = "Unrecognized data type '";
00286 message += st.type;
00287 message += '\'';
00288
00289 throw sg_io_exception(message, "SimGear Property Reader");
00290 }
00291 if (!ret)
00292 SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
00293 << st.node->getPath() << " to value \""
00294 << _data << "\" with type " << st.type);
00295 }
00296
00297
00298
00299
00300 st.node->setAttributes(st.mode);
00301
00302 pop_state();
00303 }
00304
00305 void
00306 PropsVisitor::data (const char * s, int length)
00307 {
00308 if (state().node->nChildren() == 0)
00309 _data.append(string(s, length));
00310 }
00311
00312 void
00313 PropsVisitor::warning (const char * message, int line, int column)
00314 {
00315 SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
00316 << message << " at line " << line << ", column " << column);
00317 }
00318
00319
00320
00322
00324
00325
00334 void
00335 readProperties (istream &input, SGPropertyNode * start_node,
00336 const string &base, int default_mode)
00337 {
00338 PropsVisitor visitor(start_node, base, default_mode);
00339 readXML(input, visitor, base);
00340 if (visitor.hasException())
00341 throw visitor.getException();
00342 }
00343
00344
00352 void
00353 readProperties (const string &file, SGPropertyNode * start_node,
00354 int default_mode)
00355 {
00356 PropsVisitor visitor(start_node, file, default_mode);
00357 readXML(file, visitor);
00358 if (visitor.hasException())
00359 throw visitor.getException();
00360 }
00361
00362
00371 void readProperties (const char *buf, const int size,
00372 SGPropertyNode * start_node, int default_mode)
00373 {
00374 PropsVisitor visitor(start_node, "", default_mode);
00375 readXML(buf, size, visitor);
00376 if (visitor.hasException())
00377 throw visitor.getException();
00378 }
00379
00380
00382
00384
00385 #define INDENT_STEP 2
00386
00390 static const char *
00391 getTypeName (SGPropertyNode::Type type)
00392 {
00393 switch (type) {
00394 case SGPropertyNode::UNSPECIFIED:
00395 return "unspecified";
00396 case SGPropertyNode::BOOL:
00397 return "bool";
00398 case SGPropertyNode::INT:
00399 return "int";
00400 case SGPropertyNode::LONG:
00401 return "long";
00402 case SGPropertyNode::FLOAT:
00403 return "float";
00404 case SGPropertyNode::DOUBLE:
00405 return "double";
00406 case SGPropertyNode::STRING:
00407 return "string";
00408 case SGPropertyNode::ALIAS:
00409 case SGPropertyNode::NONE:
00410 return "unspecified";
00411 }
00412
00413
00414 return "unspecified";
00415 }
00416
00417
00421 static void
00422 writeData (ostream &output, const string &data)
00423 {
00424 for (int i = 0; i < (int)data.size(); i++) {
00425 switch (data[i]) {
00426 case '&':
00427 output << "&";
00428 break;
00429 case '<':
00430 output << "<";
00431 break;
00432 case '>':
00433 output << ">";
00434 break;
00435 default:
00436 output << data[i];
00437 break;
00438 }
00439 }
00440 }
00441
00442 static void
00443 doIndent (ostream &output, int indent)
00444 {
00445 while (indent-- > 0) {
00446 output << ' ';
00447 }
00448 }
00449
00450
00451 static void
00452 writeAtts (ostream &output, const SGPropertyNode * node, bool forceindex)
00453 {
00454 int index = node->getIndex();
00455
00456 if (index != 0 || forceindex)
00457 output << " n=\"" << index << '"';
00458
00459 #if 0
00460 if (!node->getAttribute(SGPropertyNode::READ))
00461 output << " read=\"n\"";
00462
00463 if (!node->getAttribute(SGPropertyNode::WRITE))
00464 output << " write=\"n\"";
00465
00466 if (node->getAttribute(SGPropertyNode::ARCHIVE))
00467 output << " archive=\"y\"";
00468 #endif
00469
00470 }
00471
00472
00476 static bool
00477 isArchivable (const SGPropertyNode * node, SGPropertyNode::Attribute archive_flag)
00478 {
00479
00480 if (node->getAttribute(archive_flag))
00481 return true;
00482 else {
00483 int nChildren = node->nChildren();
00484 for (int i = 0; i < nChildren; i++)
00485 if (isArchivable(node->getChild(i), archive_flag))
00486 return true;
00487 }
00488 return false;
00489 }
00490
00491
00492 static bool
00493 writeNode (ostream &output, const SGPropertyNode * node,
00494 bool write_all, int indent, SGPropertyNode::Attribute archive_flag)
00495 {
00496
00497
00498
00499 if (!write_all && !isArchivable(node, archive_flag))
00500 return true;
00501
00502 const string name = node->getName();
00503 int nChildren = node->nChildren();
00504 bool node_has_value = false;
00505
00506
00507
00508 if (node->hasValue() && (write_all || node->getAttribute(archive_flag))) {
00509 doIndent(output, indent);
00510 output << '<' << name;
00511 writeAtts(output, node, nChildren != 0);
00512 if (node->isAlias() && node->getAliasTarget() != 0) {
00513 output << " alias=\"" << node->getAliasTarget()->getPath()
00514 << "\"/>" << endl;
00515 } else {
00516 if (node->getType() != SGPropertyNode::UNSPECIFIED)
00517 output << " type=\"" << getTypeName(node->getType()) << '"';
00518 output << '>';
00519 writeData(output, node->getStringValue());
00520 output << "</" << name << '>' << endl;
00521 }
00522 node_has_value = true;
00523 }
00524
00525
00526 if (nChildren > 0) {
00527 doIndent(output, indent);
00528 output << '<' << name;
00529 writeAtts(output, node, node_has_value);
00530 output << '>' << endl;
00531 for (int i = 0; i < nChildren; i++)
00532 writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP, archive_flag);
00533 doIndent(output, indent);
00534 output << "</" << name << '>' << endl;
00535 }
00536
00537 return true;
00538 }
00539
00540
00541 void
00542 writeProperties (ostream &output, const SGPropertyNode * start_node,
00543 bool write_all, SGPropertyNode::Attribute archive_flag)
00544 {
00545 int nChildren = start_node->nChildren();
00546
00547 output << "<?xml version=\"1.0\"?>" << endl << endl;
00548 output << "<PropertyList>" << endl;
00549
00550 for (int i = 0; i < nChildren; i++) {
00551 writeNode(output, start_node->getChild(i), write_all, INDENT_STEP, archive_flag);
00552 }
00553
00554 output << "</PropertyList>" << endl;
00555 }
00556
00557
00558 void
00559 writeProperties (const string &file, const SGPropertyNode * start_node,
00560 bool write_all, SGPropertyNode::Attribute archive_flag)
00561 {
00562 SGPath path(file.c_str());
00563 path.create_dir(0777);
00564
00565 ofstream output(file.c_str());
00566 if (output.good()) {
00567 writeProperties(output, start_node, write_all, archive_flag);
00568 } else {
00569 throw sg_io_exception("Cannot open file", sg_location(file));
00570 }
00571 }
00572
00573
00574
00576
00578
00579
00588 bool
00589 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
00590 {
00591 bool retval = true;
00592
00593
00594
00595 if (in->hasValue()) {
00596 switch (in->getType()) {
00597 case SGPropertyNode::BOOL:
00598 if (!out->setBoolValue(in->getBoolValue()))
00599 retval = false;
00600 break;
00601 case SGPropertyNode::INT:
00602 if (!out->setIntValue(in->getIntValue()))
00603 retval = false;
00604 break;
00605 case SGPropertyNode::LONG:
00606 if (!out->setLongValue(in->getLongValue()))
00607 retval = false;
00608 break;
00609 case SGPropertyNode::FLOAT:
00610 if (!out->setFloatValue(in->getFloatValue()))
00611 retval = false;
00612 break;
00613 case SGPropertyNode::DOUBLE:
00614 if (!out->setDoubleValue(in->getDoubleValue()))
00615 retval = false;
00616 break;
00617 case SGPropertyNode::STRING:
00618 if (!out->setStringValue(in->getStringValue()))
00619 retval = false;
00620 break;
00621 case SGPropertyNode::UNSPECIFIED:
00622 if (!out->setUnspecifiedValue(in->getStringValue()))
00623 retval = false;
00624 break;
00625 default:
00626 if (in->isAlias())
00627 break;
00628 string message = "Unknown internal SGPropertyNode type";
00629 message += in->getType();
00630 throw sg_error(message, "SimGear Property Reader");
00631 }
00632 }
00633
00634
00635 out->setAttributes( in->getAttributes() );
00636
00637
00638 int nChildren = in->nChildren();
00639 for (int i = 0; i < nChildren; i++) {
00640 const SGPropertyNode * in_child = in->getChild(i);
00641 SGPropertyNode * out_child = out->getChild(in_child->getName(),
00642 in_child->getIndex(),
00643 true);
00644 if (!copyProperties(in_child, out_child))
00645 retval = false;
00646 }
00647
00648 return retval;
00649 }
00650
00651