]> git.mxchange.org Git - simgear.git/blob - simgear/misc/props_io.cxx
Reduced the verbocity level of the property loader.
[simgear.git] / simgear / misc / props_io.cxx
1
2 #ifdef HAVE_CONFIG_H
3 #  include <config.h>
4 #endif
5
6 #include <stdlib.h>             // atof() atoi()
7
8 #include <simgear/debug/logstream.hxx>
9 #include <simgear/xml/easyxml.hxx>
10
11 #include "props.hxx"
12
13 #include <iostream>
14 #include <fstream>
15 #include <string>
16 #include <vector>
17
18 using std::istream;
19 using std::ifstream;
20 using std::ostream;
21 using std::ofstream;
22 using std::string;
23 using std::vector;
24
25
26 \f
27 ////////////////////////////////////////////////////////////////////////
28 // Property list visitor, for XML parsing.
29 ////////////////////////////////////////////////////////////////////////
30
31 class PropsVisitor : public XMLVisitor
32 {
33 public:
34
35   PropsVisitor (SGPropertyNode * root) : _ok(true), _root(root), _level(0) {}
36
37   void startXML ();
38   void endXML ();
39   void startElement (const char * name, const XMLAttributes &atts);
40   void endElement (const char * name);
41   void data (const char * s, int length);
42   void warning (const char * message, int line, int column);
43   void error (const char * message, int line, int column);
44
45   bool isOK () const { return _ok; }
46
47 private:
48
49   struct State
50   {
51     State () : node(0), type("") {}
52     State (SGPropertyNode * _node, const char * _type)
53       : node(_node), type(_type) {}
54     SGPropertyNode * node;
55     string type;
56   };
57
58   State &state () { return _state_stack[_state_stack.size() - 1]; }
59
60   void push_state (SGPropertyNode * node, const char * type) {
61     if (type == 0)
62       _state_stack.push_back(State(node, "unknown"));
63     else
64       _state_stack.push_back(State(node, type));
65     _level++;
66     _data = "";
67   }
68
69   void pop_state () {
70     _state_stack.pop_back();
71     _level--;
72   }
73
74   bool _ok;
75   string _data;
76   SGPropertyNode * _root;
77   int _level;
78   vector<State> _state_stack;
79
80 };
81
82 void
83 PropsVisitor::startXML ()
84 {
85   _level = 0;
86   _state_stack.resize(0);
87 }
88
89 void
90 PropsVisitor::endXML ()
91 {
92   _level = 0;
93   _state_stack.resize(0);
94 }
95
96 void
97 PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
98 {
99   if (_level == 0) {
100     push_state(_root, "");
101   }
102
103   else {
104     const char * att_n = atts.getValue("n");
105     int index = 0;
106     if (att_n != 0)
107       index = atoi(att_n);
108     push_state(state().node->getChild(name, index, true),
109                atts.getValue("type"));
110   }
111 }
112
113 void
114 PropsVisitor::endElement (const char * name)
115 {
116   State &st = state();
117   bool ret;
118
119                                 // If there are no children, then
120                                 // it is a leaf value.
121   if (st.node->nChildren() == 0) {
122     if (st.type == "bool") {
123       if (_data == "true" || atoi(_data.c_str()) != 0)
124         ret = st.node->setBoolValue(true);
125       else
126         ret = st.node->setBoolValue(false);
127     } else if (st.type == "int") {
128       ret = st.node->setIntValue(atoi(_data.c_str()));
129     } else if (st.type == "float") {
130       ret = st.node->setFloatValue(atof(_data.c_str()));
131     } else if (st.type == "double") {
132       ret = st.node->setDoubleValue(atof(_data.c_str()));
133     } else if (st.type == "string") {
134       ret = st.node->setStringValue(_data);
135     } else if (st.type == "unknown") {
136       ret = st.node->setUnknownValue(_data);
137     } else {
138       FG_LOG(FG_INPUT, FG_ALERT, "Unknown data type " << st.type
139              << " assuming 'unknown'");
140       ret = st.node->setUnknownValue(_data);
141     }
142     if (!ret)
143       FG_LOG(FG_INPUT, FG_ALERT, "readProperties: Failed to set "
144              << st.node->getPath() << " to value \""
145              << _data << "\" with type " << st.type);
146   }
147
148   pop_state();
149 }
150
151 void
152 PropsVisitor::data (const char * s, int length)
153 {
154   if (state().node->nChildren() == 0)
155     _data.append(string(s, length));
156 }
157
158 void
159 PropsVisitor::warning (const char * message, int line, int column)
160 {
161   FG_LOG(FG_INPUT, FG_ALERT, "readProperties: warning: "
162          << message << " at line " << line << ", column " << column);
163 }
164
165 void
166 PropsVisitor::error (const char * message, int line, int column)
167 {
168   FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: "
169          << message << " at line " << line << ", column " << column);
170   _ok = false;
171 }
172
173
174 \f
175 ////////////////////////////////////////////////////////////////////////
176 // Property list reader.
177 ////////////////////////////////////////////////////////////////////////
178
179 bool
180 readProperties (istream &input, SGPropertyNode * start_node)
181 {
182   PropsVisitor visitor(start_node);
183   return readXML(input, visitor) && visitor.isOK();
184 }
185
186 bool
187 readProperties (const string &file, SGPropertyNode * start_node)
188 {
189   ifstream input(file.c_str());
190   if (input.good()) {
191     return readProperties(input, start_node);
192   } else {
193     FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
194            << file);
195     return false;
196   }
197 }
198
199
200 \f
201 ////////////////////////////////////////////////////////////////////////
202 // Property list writer.
203 ////////////////////////////////////////////////////////////////////////
204
205 #define INDENT_STEP 2
206
207 /**
208  * Return the type name.
209  */
210 static const char *
211 getTypeName (SGValue::Type type)
212 {
213   switch (type) {
214   case SGValue::UNKNOWN:
215     return "unknown";
216   case SGValue::BOOL:
217     return "bool";
218   case SGValue::INT:
219     return "int";
220   case SGValue::FLOAT:
221     return "float";
222   case SGValue::DOUBLE:
223     return "double";
224   case SGValue::STRING:
225     return "string";
226   }
227 }
228
229
230 /**
231  * Escape characters for output.
232  */
233 static void
234 writeData (ostream &output, const string &data)
235 {
236   for (int i = 0; i < data.size(); i++) {
237     switch (data[i]) {
238     case '&':
239       output << "&amp;";
240       break;
241     case '<':
242       output << "&lt;";
243       break;
244     case '>':
245       output << "&gt;";
246       break;
247     default:
248       output << data[i];
249       break;
250     }
251   }
252 }
253
254 static void
255 doIndent (ostream &output, int indent)
256 {
257   while (indent-- > 0) {
258     output << ' ';
259   }
260 }
261
262
263 static bool
264 writeNode (ostream &output, const SGPropertyNode * node, int indent)
265 {
266   const string &name = node->getName();
267   int index = node->getIndex();
268   int nChildren = node->nChildren();
269
270                                 // If there is a literal value,
271                                 // write it first.
272   if (node->hasValue()) {
273     doIndent(output, indent);
274     output << '<' << name << " n=\"" << index
275            << "\" type=\"" << getTypeName(node->getType()) << "\">";
276     writeData(output, node->getStringValue());
277     output << "</" << name << '>' << endl;;
278   }
279
280                                 // If there are children, write them
281                                 // next.
282   if (nChildren > 0) {
283     doIndent(output, indent);
284     output << '<' << name << " n=\"" << index << "\">" << endl;;
285     for (int i = 0; i < nChildren; i++)
286       writeNode(output, node->getChild(i), indent + INDENT_STEP);
287     doIndent(output, indent);
288     output << "</" << name << '>' << endl;
289   }
290
291                                 // If there were no children and no
292                                 // value, at least note the presence
293                                 // of the node.
294   if (nChildren == 0 && !node->hasValue()) {
295     doIndent(output, indent);
296     output << '<' << name << " n=\"" << index << "\"/>" << endl;
297   }
298
299   return true;
300 }
301
302 bool
303 writeProperties (ostream &output, const SGPropertyNode * start_node)
304 {
305   int nChildren = start_node->nChildren();
306
307   output << "<?xml version=\"1.0\"?>" << endl << endl;
308   output << "<PropertyList>" << endl;
309
310   for (int i = 0; i < nChildren; i++) {
311     writeNode(output, start_node->getChild(i), INDENT_STEP);
312   }
313
314   output << "</PropertyList>" << endl;
315
316   return true;
317 }
318
319 bool
320 writeProperties (const string &file, const SGPropertyNode * start_node)
321 {
322   ofstream output(file.c_str());
323   if (output.good()) {
324     return writeProperties(output, start_node);
325   } else {
326     FG_LOG(FG_INPUT, FG_ALERT, "Cannot write properties to file "
327            << file);
328     return false;
329   }
330 }
331
332
333 /**
334  * Copy one property list to another.
335  */
336 bool
337 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
338 {
339   bool retval = true;
340
341                                 // First, copy the actual value,
342                                 // if any.
343   if (in->hasValue()) {
344     switch (in->getType()) {
345     case SGValue::BOOL:
346       if (!out->setBoolValue(in->getBoolValue()))
347         retval = false;
348       break;
349     case SGValue::INT:
350       if (!out->setIntValue(in->getIntValue()))
351         retval = false;
352       break;
353     case SGValue::FLOAT:
354       if (!out->setFloatValue(in->getFloatValue()))
355         retval = false;
356       break;
357     case SGValue::DOUBLE:
358       if (!out->setDoubleValue(in->getDoubleValue()))
359         retval = false;
360       break;
361     case SGValue::STRING:
362       if (!out->setStringValue(in->getStringValue()))
363         retval = false;
364       break;
365     case SGValue::UNKNOWN:
366       if (!out->setUnknownValue(in->getStringValue()))
367         retval = false;
368       break;
369     default:
370       throw string("Unknown SGValue type"); // FIXME!!!
371     }
372   }
373
374                                 // Next, copy the children.
375   int nChildren = in->nChildren();
376   for (int i = 0; i < nChildren; i++) {
377     const SGPropertyNode * in_child = in->getChild(i);
378     SGPropertyNode * out_child = out->getChild(in_child->getName(),
379                                                in_child->getIndex(),
380                                                true);
381     if (!copyProperties(in_child, out_child))
382       retval = false;
383   }
384
385   return retval;
386 }
387
388 // end of props_io.cxx