]> git.mxchange.org Git - simgear.git/blob - simgear/misc/props_io.cxx
Irix tweaks.
[simgear.git] / simgear / misc / props_io.cxx
1
2 #ifdef HAVE_CONFIG_H
3 #  include <config.h>
4 #endif
5
6 #include <simgear/compiler.h>
7
8 #include <stdlib.h>             // atof() atoi()
9
10 #include <simgear/sg_inlines.h>
11 #include <simgear/debug/logstream.hxx>
12 #include <simgear/xml/easyxml.hxx>
13
14 #include "sg_path.hxx"
15 #include "props.hxx"
16
17 #include STL_IOSTREAM
18 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
19 #  include <fstream>
20 #else
21 #  include <fstream.h>
22 #endif
23 #include STL_STRING
24 #include <vector>
25 #include <map>
26
27 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
28 SG_USING_STD(istream);
29 SG_USING_STD(ifstream);
30 SG_USING_STD(ostream);
31 SG_USING_STD(ofstream);
32 #endif
33 SG_USING_STD(string);
34 SG_USING_STD(vector);
35 SG_USING_STD(map);
36
37
38 \f
39 ////////////////////////////////////////////////////////////////////////
40 // Property list visitor, for XML parsing.
41 ////////////////////////////////////////////////////////////////////////
42
43 class PropsVisitor : public XMLVisitor
44 {
45 public:
46
47   PropsVisitor (SGPropertyNode * root, const string &base)
48     : _ok(true), _root(root), _level(0), _base(base) {}
49
50   void startXML ();
51   void endXML ();
52   void startElement (const char * name, const XMLAttributes &atts);
53   void endElement (const char * name);
54   void data (const char * s, int length);
55   void warning (const char * message, int line, int column);
56   void error (const char * message, int line, int column);
57
58   bool isOK () const { return _ok; }
59
60 private:
61
62   struct State
63   {
64     State () : node(0), type("") {}
65     State (SGPropertyNode * _node, const char * _type)
66       : node(_node), type(_type) {}
67     SGPropertyNode * node;
68     string type;
69     map<string,int> counters;
70   };
71
72   State &state () { return _state_stack[_state_stack.size() - 1]; }
73
74   void push_state (SGPropertyNode * node, const char * type) {
75     if (type == 0)
76       _state_stack.push_back(State(node, "unknown"));
77     else
78       _state_stack.push_back(State(node, type));
79     _level++;
80     _data = "";
81   }
82
83   void pop_state () {
84     _state_stack.pop_back();
85     _level--;
86   }
87
88   bool _ok;
89   string _data;
90   SGPropertyNode * _root;
91   int _level;
92   vector<State> _state_stack;
93   string _base;
94 };
95
96 void
97 PropsVisitor::startXML ()
98 {
99   _level = 0;
100   _state_stack.resize(0);
101 }
102
103 void
104 PropsVisitor::endXML ()
105 {
106   _level = 0;
107   _state_stack.resize(0);
108 }
109
110 void
111 PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
112 {
113   State &st = state();
114
115   if (_level == 0) {
116     if (string(name) != (string)"PropertyList") {
117       SG_LOG(SG_INPUT, SG_ALERT, "Root element name is " <<
118              name << "; expected PropertyList");
119       _ok = false;
120     }
121     push_state(_root, "");
122   }
123
124   else {
125                                 // Get the index.
126     const char * att_n = atts.getValue("n");
127     int index = 0;
128     if (att_n != 0) {
129       index = atoi(att_n);
130       st.counters[name] = SG_MAX2(st.counters[name], index+1);
131     } else {
132       index = st.counters[name];
133       st.counters[name]++;
134     }
135
136                                 // Check for an alias.
137     SGPropertyNode * node = st.node->getChild(name, index, true);
138     const char * att_alias = atts.getValue("alias");
139     if (att_alias != 0) {
140       if (!node->alias(att_alias))
141         SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << att_alias);
142     }
143
144                                 // Check for an include.
145     const char * att_include = atts.getValue("include");
146     if (att_include != 0) {
147       SGPath path(SGPath(_base).dir());
148       cerr << "Base is " << _base << endl;
149       cerr << "Dir is " << SGPath(_base).dir() << endl;
150       path.append(att_include);
151       if (!readProperties(path.str(), node)) {
152         SG_LOG(SG_INPUT, SG_ALERT, "Failed to read include file "
153                << att_include);
154         _ok = false;
155       }
156     }
157
158     push_state(node, atts.getValue("type"));
159   }
160 }
161
162 void
163 PropsVisitor::endElement (const char * name)
164 {
165   State &st = state();
166   bool ret;
167
168                                 // If there are no children and it's
169                                 // not an alias, then it's a leaf value.
170   if (st.node->nChildren() == 0 && !st.node->isAlias()) {
171     if (st.type == "bool") {
172       if (_data == "true" || atoi(_data.c_str()) != 0)
173         ret = st.node->setBoolValue(true);
174       else
175         ret = st.node->setBoolValue(false);
176     } else if (st.type == "int") {
177       ret = st.node->setIntValue(atoi(_data.c_str()));
178     } else if (st.type == "long") {
179       ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
180     } else if (st.type == "float") {
181       ret = st.node->setFloatValue(atof(_data.c_str()));
182     } else if (st.type == "double") {
183       ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
184     } else if (st.type == "string") {
185       ret = st.node->setStringValue(_data);
186     } else if (st.type == "unknown") {
187       ret = st.node->setUnknownValue(_data);
188     } else {
189       SG_LOG(SG_INPUT, SG_ALERT, "Unknown data type " << st.type
190              << " assuming 'unknown'");
191       ret = st.node->setUnknownValue(_data);
192     }
193     if (!ret)
194       SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
195              << st.node->getPath() << " to value \""
196              << _data << "\" with type " << st.type);
197   }
198
199   pop_state();
200 }
201
202 void
203 PropsVisitor::data (const char * s, int length)
204 {
205   if (state().node->nChildren() == 0)
206     _data.append(string(s, length));
207 }
208
209 void
210 PropsVisitor::warning (const char * message, int line, int column)
211 {
212   SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
213          << message << " at line " << line << ", column " << column);
214 }
215
216 void
217 PropsVisitor::error (const char * message, int line, int column)
218 {
219   SG_LOG(SG_INPUT, SG_ALERT, "readProperties: FATAL: " <<
220          message << " at line " << line << ", column " << column);
221   _ok = false;
222 }
223
224
225 \f
226 ////////////////////////////////////////////////////////////////////////
227 // Property list reader.
228 ////////////////////////////////////////////////////////////////////////
229
230
231 /**
232  * Read properties from an input stream.
233  *
234  * @param input The input stream containing an XML property file.
235  * @param start_node The root node for reading properties.
236  * @param base A base path for resolving external include references.
237  * @return true if the read succeeded, false otherwise.
238  */
239 bool
240 readProperties (istream &input, SGPropertyNode * start_node,
241                 const string &base)
242 {
243   PropsVisitor visitor(start_node, base);
244   return readXML(input, visitor) && visitor.isOK();
245 }
246
247
248 /**
249  * Read properties from a file.
250  *
251  * @param file A string containing the file path.
252  * @param start_node The root node for reading properties.
253  * @return true if the read succeeded, false otherwise.
254  */
255 bool
256 readProperties (const string &file, SGPropertyNode * start_node)
257 {
258   cerr << "Reading properties from " << file << endl;
259   ifstream input(file.c_str());
260   if (input.good()) {
261     return readProperties(input, start_node, file);
262   } else {
263     SG_LOG(SG_INPUT, SG_ALERT, "Error reading property list from file "
264            << file);
265     return false;
266   }
267 }
268
269
270 \f
271 ////////////////////////////////////////////////////////////////////////
272 // Property list writer.
273 ////////////////////////////////////////////////////////////////////////
274
275 #define INDENT_STEP 2
276
277 /**
278  * Return the type name.
279  */
280 static const char *
281 getTypeName (SGValue::Type type)
282 {
283   switch (type) {
284   case SGValue::UNKNOWN:
285     return "unknown";
286   case SGValue::BOOL:
287     return "bool";
288   case SGValue::INT:
289     return "int";
290   case SGValue::LONG:
291     return "long";
292   case SGValue::FLOAT:
293     return "float";
294   case SGValue::DOUBLE:
295     return "double";
296   case SGValue::STRING:
297     return "string";
298   }
299
300   // keep the compiler from squawking
301   return "unknown";
302 }
303
304
305 /**
306  * Escape characters for output.
307  */
308 static void
309 writeData (ostream &output, const string &data)
310 {
311   for (int i = 0; i < (int)data.size(); i++) {
312     switch (data[i]) {
313     case '&':
314       output << "&amp;";
315       break;
316     case '<':
317       output << "&lt;";
318       break;
319     case '>':
320       output << "&gt;";
321       break;
322     default:
323       output << data[i];
324       break;
325     }
326   }
327 }
328
329 static void
330 doIndent (ostream &output, int indent)
331 {
332   while (indent-- > 0) {
333     output << ' ';
334   }
335 }
336
337
338 static bool
339 writeNode (ostream &output, const SGPropertyNode * node, int indent)
340 {
341   const string &name = node->getName();
342   int index = node->getIndex();
343   int nChildren = node->nChildren();
344
345                                 // If there is a literal value,
346                                 // write it first.
347   if (node->hasValue()) {
348     doIndent(output, indent);
349     output << '<' << name << " n=\"" << index;
350     if (node->isAlias() && node->getAliasTarget() != 0) {
351       output << "\" alias=\""
352              << node->getAliasTarget()->getPath() << "\"/>" << endl;
353     } else {
354       if (node->getType() != SGValue::UNKNOWN)
355         output << "\" type=\"" << getTypeName(node->getType()) << '"';
356       output << '>';
357       writeData(output, node->getStringValue());
358       output << "</" << name << '>' << endl;;
359     }
360   }
361
362                                 // If there are children, write them
363                                 // next.
364   if (nChildren > 0) {
365     doIndent(output, indent);
366     output << '<' << name << " n=\"" << index << "\">" << endl;;
367     for (int i = 0; i < nChildren; i++)
368       writeNode(output, node->getChild(i), indent + INDENT_STEP);
369     doIndent(output, indent);
370     output << "</" << name << '>' << endl;
371   }
372
373                                 // If there were no children and no
374                                 // value, at least note the presence
375                                 // of the node.
376   if (nChildren == 0 && !node->hasValue()) {
377     doIndent(output, indent);
378     output << '<' << name << " n=\"" << index << "\"/>" << endl;
379   }
380
381   return true;
382 }
383
384
385 /**
386  * Write a property tree to an output stream in XML format.
387  *
388  * @param output The output stream.
389  * @param start_node The root node to write.
390  * @return true if the write succeeded, false otherwise.
391  */
392 bool
393 writeProperties (ostream &output, const SGPropertyNode * start_node)
394 {
395   int nChildren = start_node->nChildren();
396
397   output << "<?xml version=\"1.0\"?>" << endl << endl;
398   output << "<PropertyList>" << endl;
399
400   for (int i = 0; i < nChildren; i++) {
401     writeNode(output, start_node->getChild(i), INDENT_STEP);
402   }
403
404   output << "</PropertyList>" << endl;
405
406   return true;
407 }
408
409
410 /**
411  * Write a property tree to a file in XML format.
412  *
413  * @param file The destination file.
414  * @param start_node The root node to write.
415  * @return true if the write succeeded, false otherwise.
416  */
417 bool
418 writeProperties (const string &file, const SGPropertyNode * start_node)
419 {
420   ofstream output(file.c_str());
421   if (output.good()) {
422     return writeProperties(output, start_node);
423   } else {
424     SG_LOG(SG_INPUT, SG_ALERT, "Cannot write properties to file "
425            << file);
426     return false;
427   }
428 }
429
430
431 \f
432 ////////////////////////////////////////////////////////////////////////
433 // Copy properties from one tree to another.
434 ////////////////////////////////////////////////////////////////////////
435
436
437 /**
438  * Copy one property tree to another.
439  * 
440  * @param in The source property tree.
441  * @param out The destination property tree.
442  * @return true if all properties were copied, false if some failed
443  *  (for example, if the property's value is tied read-only).
444  */
445 bool
446 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
447 {
448   bool retval = true;
449
450                                 // First, copy the actual value,
451                                 // if any.
452   if (in->hasValue()) {
453     switch (in->getType()) {
454     case SGValue::BOOL:
455       if (!out->setBoolValue(in->getBoolValue()))
456         retval = false;
457       break;
458     case SGValue::INT:
459       if (!out->setIntValue(in->getIntValue()))
460         retval = false;
461       break;
462     case SGValue::LONG:
463       if (!out->setLongValue(in->getLongValue()))
464         retval = false;
465       break;
466     case SGValue::FLOAT:
467       if (!out->setFloatValue(in->getFloatValue()))
468         retval = false;
469       break;
470     case SGValue::DOUBLE:
471       if (!out->setDoubleValue(in->getDoubleValue()))
472         retval = false;
473       break;
474     case SGValue::STRING:
475       if (!out->setStringValue(in->getStringValue()))
476         retval = false;
477       break;
478     case SGValue::UNKNOWN:
479       if (!out->setUnknownValue(in->getStringValue()))
480         retval = false;
481       break;
482     default:
483       throw string("Unknown SGValue type");
484     }
485   }
486
487                                 // Next, copy the children.
488   int nChildren = in->nChildren();
489   for (int i = 0; i < nChildren; i++) {
490     const SGPropertyNode * in_child = in->getChild(i);
491     SGPropertyNode * out_child = out->getChild(in_child->getName(),
492                                                in_child->getIndex(),
493                                                true);
494     if (!copyProperties(in_child, out_child))
495       retval = false;
496   }
497
498   return retval;
499 }
500
501 // end of props_io.cxx