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