props_io.cxx

Go to the documentation of this file.
00001 
00011 #ifdef HAVE_CONFIG_H
00012 #  include <simgear_config.h>
00013 #endif
00014 
00015 #include <simgear/compiler.h>
00016 
00017 #include <stdlib.h>             // atof() atoi()
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 // Property list visitor, for XML parsing.
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                                 // FIXME: add location info
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                                 // Check for an include.
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                                 // Get the index.
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                                 // Got the index, so grab the node.
00189     SGPropertyNode * node = st.node->getChild(name, index, true);
00190 
00191                                 // Get the access-mode attributes,
00192                                 // but don't set yet (in case they
00193                                 // prevent us from recording the value).
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                                 // Check for an alias.
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                                 // Check for an include.
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                                 // If there are no children and it's
00263                                 // not an alias, then it's a leaf value.
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;               // empty <PropertyList>
00284     } else {
00285       string message = "Unrecognized data type '";
00286       message += st.type;
00287       message += '\'';
00288                                 // FIXME: add location information
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                                 // Set the access-mode attributes now,
00298                                 // once the value has already been 
00299                                 // assigned.
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 // Property list reader.
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 // Property list writer.
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   // keep the compiler from squawking
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 << "&amp;";
00428       break;
00429     case '<':
00430       output << "&lt;";
00431       break;
00432     case '>':
00433       output << "&gt;";
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   // FIXME: it's inefficient to do this all the time
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                                 // Don't write the node or any of
00497                                 // its descendants unless it is
00498                                 // allowed to be archived.
00499   if (!write_all && !isArchivable(node, archive_flag))
00500     return true;                // Everything's OK, but we won't write.
00501 
00502   const string name = node->getName();
00503   int nChildren = node->nChildren();
00504   bool node_has_value = false;
00505 
00506                                 // If there is a literal value,
00507                                 // write it first.
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                                 // If there are children, write them next.
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 // Copy properties from one tree to another.
00578 
00579 
00588 bool
00589 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
00590 {
00591   bool retval = true;
00592 
00593                                 // First, copy the actual value,
00594                                 // if any.
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                                 // copy the attributes.
00635   out->setAttributes( in->getAttributes() );
00636 
00637                                 // Next, copy the children.
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 // end of props_io.cxx

Generated on Mon Dec 17 09:30:55 2007 for SimGear by  doxygen 1.5.1