head 1.4; access; symbols; locks; strict; comment @# @; 1.4 date 2002.09.07.01.50.39; author curt; state Exp; branches; next 1.3; 1.3 date 2002.08.31.19.01.56; author curt; state Exp; branches; next 1.2; 1.2 date 2002.08.31.18.54.03; author curt; state Exp; branches; next 1.1; 1.1 date 2002.02.15.17.21.59; author curt; state Exp; branches; next ; desc @@ 1.4 log @Updated doxygen docs. @ text @
Main Page Class Hierarchy Compound List File List Compound Members File Members

# props_io.cxx

    00001
    00002 #include <simgear/compiler.h>
    00003
    00004 #include <stdlib.h>             // atof() atoi()
    00005
    00006 #include <simgear/sg_inlines.h>
    00007 #include <simgear/debug/logstream.hxx>
    00008 #include <simgear/xml/easyxml.hxx>
    00009
    00010 #include "sg_path.hxx"
    00011 #include "props.hxx"
    00012 #include "props_io.hxx"
    00013
    00014 #include STL_IOSTREAM
    00015 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
    00016 #  include <fstream>
    00017 #else
    00018 #  include <fstream.h>
    00019 #endif
    00020 #include STL_STRING
    00021 #include <vector>
    00022 #include <map>
    00023
    00024 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
    00025 SG_USING_STD(istream);
    00026 SG_USING_STD(ifstream);
    00027 SG_USING_STD(ostream);
    00028 SG_USING_STD(ofstream);
    00029 #endif
    00030 SG_USING_STD(string);
    00031 SG_USING_STD(vector);
    00032 SG_USING_STD(map);
    00033
    00034 #define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
    00035
    00036
    00037
    00039 // Property list visitor, for XML parsing.
    00041
    00042 class PropsVisitor : public XMLVisitor
    00043 {
    00044 public:
    00045
    00046   PropsVisitor (SGPropertyNode * root, const string &base)
    00047     : _root(root), _level(0), _base(base), _hasException(false) {}
    00048
    00049   virtual ~PropsVisitor () {}
    00050
    00051   void startXML ();
    00052   void endXML ();
    00053   void startElement (const char * name, const XMLAttributes &atts);
    00054   void endElement (const char * name);
    00055   void data (const char * s, int length);
    00056   void warning (const char * message, int line, int column);
    00057
    00058   bool hasException () const { return _hasException; }
    00059   sg_io_exception &getException () { return _exception; }
    00060   void setException (const sg_io_exception &exception) {
    00061     _exception = exception;
    00062     _hasException = true;
    00063   }
    00064
    00065 private:
    00066
    00067   struct State
    00068   {
    00069     State () : node(0), type(""), mode(DEFAULT_MODE) {}
    00070     State (SGPropertyNode * _node, const char * _type, int _mode)
    00071       : node(_node), type(_type), mode(_mode) {}
    00072     SGPropertyNode * node;
    00073     string type;
    00074     int mode;
    00075     map<string,int> counters;
    00076   };
    00077
    00078   State &state () { return _state_stack[_state_stack.size() - 1]; }
    00079
    00080   void push_state (SGPropertyNode * node, const char * type, int mode) {
    00081     if (type == 0)
    00082       _state_stack.push_back(State(node, "unspecified", mode));
    00083     else
    00084       _state_stack.push_back(State(node, type, mode));
    00085     _level++;
    00086     _data = "";
    00087   }
    00088
    00089   void pop_state () {
    00090     _state_stack.pop_back();
    00091     _level--;
    00092   }
    00093
    00094   string _data;
    00095   SGPropertyNode * _root;
    00096   int _level;
    00097   vector<State> _state_stack;
    00098   string _base;
    00099   sg_io_exception _exception;
    00100   bool _hasException;
    00101 };
    00102
    00103 void
    00104 PropsVisitor::startXML ()
    00105 {
    00106   _level = 0;
    00107   _state_stack.resize(0);
    00108 }
    00109
    00110 void
    00111 PropsVisitor::endXML ()
    00112 {
    00113   _level = 0;
    00114   _state_stack.resize(0);
    00115 }
    00116
    00117
    00121 static bool
    00122 checkFlag (const char * flag, bool defaultState = true)
    00123 {
    00124   if (flag == 0)
    00125     return defaultState;
    00126   else if (string(flag) == "y")
    00127     return true;
    00128   else if (string(flag) == "n")
    00129     return false;
    00130   else {
    00131     string message = "Unrecognized flag value '";
    00132     message += flag;
    00133     message += '\'';
    00134                                 // FIXME: add location info
    00135     throw sg_io_exception(message, "SimGear Property Reader");
    00136   }
    00137 }
    00138
    00139 void
    00140 PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
    00141 {
    00142   State &st = state();
    00143
    00144   if (_level == 0) {
    00145     if (string(name) != (string)"PropertyList") {
    00146       string message = "Root element name is ";
    00147       message += name;
    00148       message += "; expected PropertyList";
    00149       throw sg_io_exception(message, "SimGear Property Reader");
    00150     }
    00151     push_state(_root, "", DEFAULT_MODE);
    00152   }
    00153
    00154   else {
    00155
    00156     const char * attval;
    00157                                 // Get the index.
    00158     attval = atts.getValue("n");
    00159     int index = 0;
    00160     if (attval != 0) {
    00161       index = atoi(attval);
    00162       st.counters[name] = SG_MAX2(st.counters[name], index+1);
    00163     } else {
    00164       index = st.counters[name];
    00165       st.counters[name]++;
    00166     }
    00167
    00168                                 // Got the index, so grab the node.
    00169     SGPropertyNode * node = st.node->getChild(name, index, true);
    00170
    00171                                 // Get the access-mode attributes,
    00172                                 // but don't set yet (in case they
    00173                                 // prevent us from recording the value).
    00174     int mode = 0;
    00175
    00176     attval = atts.getValue("read");
    00177     if (checkFlag(attval, true))
    00178       mode |= SGPropertyNode::READ;
    00179     attval = atts.getValue("write");
    00180     if (checkFlag(attval, true))
    00181       mode |= SGPropertyNode::WRITE;
    00182     attval = atts.getValue("archive");
    00183     if (checkFlag(attval, false))
    00184       mode |= SGPropertyNode::ARCHIVE;
    00185     attval = atts.getValue("trace-read");
    00186     if (checkFlag(attval, false))
    00187       mode |= SGPropertyNode::TRACE_READ;
    00188     attval = atts.getValue("trace-write");
    00189     if (checkFlag(attval, false))
    00190       mode |= SGPropertyNode::TRACE_WRITE;
    00191
    00192                                 // Check for an alias.
    00193     attval = atts.getValue("alias");
    00194     if (attval != 0) {
    00195       if (!node->alias(attval))
    00196         SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
    00197     }
    00198
    00199                                 // Check for an include.
    00200     attval = atts.getValue("include");
    00201     if (attval != 0) {
    00202       SGPath path(SGPath(_base).dir());
    00203       path.append(attval);
    00204       try {
    00205         readProperties(path.str(), node);
    00206       } catch (sg_io_exception &e) {
    00207         setException(e);
    00208       }
    00209     }
    00210
    00211     push_state(node, atts.getValue("type"), mode);
    00212   }
    00213 }
    00214
    00215 void
    00216 PropsVisitor::endElement (const char * name)
    00217 {
    00218   State &st = state();
    00219   bool ret;
    00220
    00221                                 // If there are no children and it's
    00222                                 // not an alias, then it's a leaf value.
    00223   if (st.node->nChildren() == 0 && !st.node->isAlias()) {
    00224     if (st.type == "bool") {
    00225       if (_data == "true" || atoi(_data.c_str()) != 0)
    00226         ret = st.node->setBoolValue(true);
    00227       else
    00228         ret = st.node->setBoolValue(false);
    00229     } else if (st.type == "int") {
    00230       ret = st.node->setIntValue(atoi(_data.c_str()));
    00231     } else if (st.type == "long") {
    00232       ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
    00233     } else if (st.type == "float") {
    00234       ret = st.node->setFloatValue(atof(_data.c_str()));
    00235     } else if (st.type == "double") {
    00236       ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
    00237     } else if (st.type == "string") {
    00238       ret = st.node->setStringValue(_data.c_str());
    00239     } else if (st.type == "unspecified") {
    00240       ret = st.node->setUnspecifiedValue(_data.c_str());
    00241     } else {
    00242       string message = "Unrecognized data type '";
    00243       message += st.type;
    00244       message += '\'';
    00245                                 // FIXME: add location information
    00246       throw sg_io_exception(message, "SimGear Property Reader");
    00247     }
    00248     if (!ret)
    00249       SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
    00250              << st.node->getPath() << " to value \""
    00251              << _data << "\" with type " << st.type);
    00252   }
    00253
    00254                                 // Set the access-mode attributes now,
    00255                                 // once the value has already been
    00256                                 // assigned.
    00257   st.node->setAttributes(st.mode);
    00258
    00259   pop_state();
    00260 }
    00261
    00262 void
    00263 PropsVisitor::data (const char * s, int length)
    00264 {
    00265   if (state().node->nChildren() == 0)
    00266     _data.append(string(s, length));
    00267 }
    00268
    00269 void
    00270 PropsVisitor::warning (const char * message, int line, int column)
    00271 {
    00272   SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
    00273          << message << " at line " << line << ", column " << column);
    00274 }
    00275
    00276
    00277
    00279 // Property list reader.
    00281
    00282
    00291 void
    00292 readProperties (istream &input, SGPropertyNode * start_node,
    00293                 const string &base)
    00294 {
    00295   PropsVisitor visitor(start_node, base);
    00296   readXML(input, visitor, base);
    00297   if (visitor.hasException())
    00298     throw visitor.getException();
    00299 }
    00300
    00301
    00309 void
    00310 readProperties (const string &file, SGPropertyNode * start_node)
    00311 {
    00312   PropsVisitor visitor(start_node, file);
    00313   readXML(file, visitor);
    00314   if (visitor.hasException())
    00315     throw visitor.getException();
    00316 }
    00317
    00318
    00319
    00321 // Property list writer.
    00323
    00324 #define INDENT_STEP 2
    00325
    00329 static const char *
    00330 getTypeName (SGPropertyNode::Type type)
    00331 {
    00332   switch (type) {
    00333   case SGPropertyNode::UNSPECIFIED:
    00334     return "unspecified";
    00335   case SGPropertyNode::BOOL:
    00336     return "bool";
    00337   case SGPropertyNode::INT:
    00338     return "int";
    00339   case SGPropertyNode::LONG:
    00340     return "long";
    00341   case SGPropertyNode::FLOAT:
    00342     return "float";
    00343   case SGPropertyNode::DOUBLE:
    00344     return "double";
    00345   case SGPropertyNode::STRING:
    00346     return "string";
    00347   case SGPropertyNode::ALIAS:
    00348   case SGPropertyNode::NONE:
    00349     return "unspecified";
    00350   }
    00351
    00352   // keep the compiler from squawking
    00353   return "unspecified";
    00354 }
    00355
    00356
    00360 static void
    00361 writeData (ostream &output, const string &data)
    00362 {
    00363   for (int i = 0; i < (int)data.size(); i++) {
    00364     switch (data[i]) {
    00365     case '&':
    00366       output << "&amp;";
    00367       break;
    00368     case '<':
    00369       output << "&lt;";
    00370       break;
    00371     case '>':
    00372       output << "&gt;";
    00373       break;
    00374     default:
    00375       output << data[i];
    00376       break;
    00377     }
    00378   }
    00379 }
    00380
    00381 static void
    00382 doIndent (ostream &output, int indent)
    00383 {
    00384   while (indent-- > 0) {
    00385     output << ' ';
    00386   }
    00387 }
    00388
    00389
    00390 static void
    00391 writeAtts (ostream &output, const SGPropertyNode * node)
    00392 {
    00393   int index = node->getIndex();
    00394
    00395   if (index != 0)
    00396     output << " n=\"" << index << '"';
    00397
    00398 #if 0
    00399   if (!node->getAttribute(SGPropertyNode::READ))
    00400     output << " read=\"n\"";
    00401
    00402   if (!node->getAttribute(SGPropertyNode::WRITE))
    00403     output << " write=\"n\"";
    00404
    00405   if (node->getAttribute(SGPropertyNode::ARCHIVE))
    00406     output << " archive=\"y\"";
    00407 #endif
    00408
    00409 }
    00410
    00411
    00415 static bool
    00416 isArchivable (const SGPropertyNode * node)
    00417 {
    00418   // FIXME: it's inefficient to do this all the time
    00419   if (node->getAttribute(SGPropertyNode::ARCHIVE))
    00420     return true;
    00421   else {
    00422     int nChildren = node->nChildren();
    00423     for (int i = 0; i < nChildren; i++)
    00424       if (isArchivable(node->getChild(i)))
    00425         return true;
    00426   }
    00427   return false;
    00428 }
    00429
    00430
    00431 static bool
    00432 writeNode (ostream &output, const SGPropertyNode * node,
    00433            bool write_all, int indent)
    00434 {
    00435                                 // Don't write the node or any of
    00436                                 // its descendants unless it is
    00437                                 // allowed to be archived.
    00438   if (!write_all && !isArchivable(node))
    00439     return true;                // Everything's OK, but we won't write.
    00440
    00441   const string name = node->getName();
    00442   int nChildren = node->nChildren();
    00443
    00444                                 // If there is a literal value,
    00445                                 // write it first.
    00446   if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) {
    00447     doIndent(output, indent);
    00448     output << '<' << name;
    00449     writeAtts(output, node);
    00450     if (node->isAlias() && node->getAliasTarget() != 0) {
    00451       output << " alias=\"" << node->getAliasTarget()->getPath()
    00452              << "\"/>" << endl;
    00453     } else {
    00454       if (node->getType() != SGPropertyNode::UNSPECIFIED)
    00455         output << " type=\"" << getTypeName(node->getType()) << '"';
    00456       output << '>';
    00457       writeData(output, node->getStringValue());
    00458       output << "</" << name << '>' << endl;
    00459     }
    00460   }
    00461
    00462                                 // If there are children, write them next.
    00463   if (nChildren > 0 || node->isAlias()) {
    00464     doIndent(output, indent);
    00465     output << '<' << name;
    00466     writeAtts(output, node);
    00467     output << '>' << endl;
    00468     for (int i = 0; i < nChildren; i++)
    00469       writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP);
    00470     doIndent(output, indent);
    00471     output << "</" << name << '>' << endl;
    00472   }
    00473
    00474   return true;
    00475 }
    00476
    00477
    00478 void
    00479 writeProperties (ostream &output, const SGPropertyNode * start_node,
    00480                  bool write_all)
    00481 {
    00482   int nChildren = start_node->nChildren();
    00483
    00484   output << "<?xml version=\"1.0\"?>" << endl << endl;
    00485   output << "<PropertyList>" << endl;
    00486
    00487   for (int i = 0; i < nChildren; i++) {
    00488     writeNode(output, start_node->getChild(i), write_all, INDENT_STEP);
    00489   }
    00490
    00491   output << "</PropertyList>" << endl;
    00492 }
    00493
    00494
    00495 void
    00496 writeProperties (const string &file, const SGPropertyNode * start_node,
    00497                  bool write_all)
    00498 {
    00499   ofstream output(file.c_str());
    00500   if (output.good()) {
    00501     writeProperties(output, start_node, write_all);
    00502   } else {
    00503     throw sg_io_exception("Cannot open file", sg_location(file));
    00504   }
    00505 }
    00506
    00507
    00508
    00510 // Copy properties from one tree to another.
    00512
    00513
    00522 bool
    00523 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
    00524 {
    00525   bool retval = true;
    00526
    00527                                 // First, copy the actual value,
    00528                                 // if any.
    00529   if (in->hasValue()) {
    00530     switch (in->getType()) {
    00531     case SGPropertyNode::BOOL:
    00532       if (!out->setBoolValue(in->getBoolValue()))
    00533         retval = false;
    00534       break;
    00535     case SGPropertyNode::INT:
    00536       if (!out->setIntValue(in->getIntValue()))
    00537         retval = false;
    00538       break;
    00539     case SGPropertyNode::LONG:
    00540       if (!out->setLongValue(in->getLongValue()))
    00541         retval = false;
    00542       break;
    00543     case SGPropertyNode::FLOAT:
    00544       if (!out->setFloatValue(in->getFloatValue()))
    00545         retval = false;
    00546       break;
    00547     case SGPropertyNode::DOUBLE:
    00548       if (!out->setDoubleValue(in->getDoubleValue()))
    00549         retval = false;
    00550       break;
    00551     case SGPropertyNode::STRING:
    00552       if (!out->setStringValue(in->getStringValue()))
    00553         retval = false;
    00554       break;
    00555     case SGPropertyNode::UNSPECIFIED:
    00556       if (!out->setUnspecifiedValue(in->getStringValue()))
    00557         retval = false;
    00558       break;
    00559     default:
    00560       string message = "Unknown internal SGPropertyNode type";
    00561       message += in->getType();
    00562       throw sg_error(message, "SimGear Property Reader");
    00563     }
    00564   }
    00565
    00566                                 // Next, copy the children.
    00567   int nChildren = in->nChildren();
    00568   for (int i = 0; i < nChildren; i++) {
    00569     const SGPropertyNode * in_child = in->getChild(i);
    00570     SGPropertyNode * out_child = out->getChild(in_child->getName(),
    00571                                                in_child->getIndex(),
    00572                                                true);
    00573     if (!copyProperties(in_child, out_child))
    00574       retval = false;
    00575   }
    00576
    00577   return retval;
    00578 }
    00579
    00580 // end of props_io.cxx

@ 1.3 log @Oops, try again. @ text @d546 1 a546 1