]> git.mxchange.org Git - simgear.git/blob - simgear/misc/props_io.cxx
#includes based off of srcdir rather than builddir.
[simgear.git] / simgear / misc / props_io.cxx
1
2 #ifdef HAVE_CONFIG_H
3 #  include <config.h>
4 #endif
5
6 #include <simgear/debug/logstream.hxx>
7 #include <simgear/xml/easyxml.hxx>
8
9 #include "props.hxx"
10
11 #include <iostream>
12 #include <fstream>
13 #include <string>
14 #include <vector>
15
16 using std::istream;
17 using std::ifstream;
18 using std::ostream;
19 using std::string;
20 using std::vector;
21
22 \f
23 ////////////////////////////////////////////////////////////////////////
24 // Visitor class for building the property list.
25 ////////////////////////////////////////////////////////////////////////
26
27 class PropVisitor : public XMLVisitor
28 {
29 public:
30   PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {}
31   void startDocument ();
32   void startElement (const char * name, const XMLAttributes &atts);
33   void endElement (const char * name);
34   void data (const char * s, int length);
35   void warning (const char * message, int line, int col);
36   void error (const char * message, int line, int col);
37
38   bool isOK () const { return _ok; }
39
40 private:
41
42   void pushState (const char * name) {
43     _states.push_back(_state);
44     _level++;
45     _state.name = name;
46     _state.type = SGValue::UNKNOWN;
47     _state.data = "";
48     _state.hasChildren = false;
49     _state.hasData = false;
50   }
51
52   void popState () {
53     _state = _states.back();
54     _states.pop_back();
55     _level--;
56   }
57
58   struct State
59   {
60     State () : hasChildren(false), hasData(false) {}
61     string name;
62     SGValue::Type type;
63     string data;
64     bool hasChildren;
65     bool hasData;
66   };
67
68   SGPropertyList * _props;
69   State _state;
70   vector<State> _states;
71   int _level;
72   bool _ok;
73 };
74
75 void
76 PropVisitor::startDocument ()
77 {
78   _level = 0;
79   _ok = true;
80 }
81
82
83 void
84 PropVisitor::startElement (const char * name, const XMLAttributes &atts)
85 {
86   if (!_ok)
87     return;
88
89   if (_level == 0 && strcmp(name, "PropertyList")) {
90     _ok = false;
91     FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \""
92            << name << "\" instead of \"PropertyList\"");
93     return;
94   }
95
96                                 // Mixed content?
97   _state.hasChildren = true;
98   if (_state.hasData) {
99     FG_LOG(FG_INPUT, FG_ALERT,
100            "XML element has mixed elements and data in element "
101            << _state.name);
102     _ok = false;
103     return;
104   }
105
106                                 // Start a new state.
107   pushState(name);
108
109                                 // See if there's a type specified.
110   const char * type = atts.getValue("type");
111   if (type == 0 || !strcmp("unknown", type))
112     _state.type = SGValue::UNKNOWN;
113   else if (!strcmp("bool", type))
114     _state.type = SGValue::BOOL;
115   else if (!strcmp("int", type))
116     _state.type = SGValue::INT;
117   else if (!strcmp("float", type))
118     _state.type = SGValue::FLOAT;
119   else if (!strcmp("double", type))
120     _state.type = SGValue::DOUBLE;
121   else if (!strcmp("string", type))
122     _state.type = SGValue::STRING;
123   else
124     FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type
125            << ", using UNKNOWN");
126 }
127
128 void
129 PropVisitor::endElement (const char * name)
130 {
131   if (!_ok)
132     return;
133
134                                 // See if there's a property to add.
135   if (_state.hasData) {
136     bool status = false;
137
138                                 // Figure out the path name.
139     string path = "";
140     for (int i = 2; i < _level; i++) {
141       path += '/';
142       path += _states[i].name;
143     }
144     path += '/';
145     path += _state.name;
146
147                                 // Set the value
148     switch (_state.type) {
149     case SGValue::BOOL:
150       if (_state.data == "true" || _state.data == "TRUE") {
151         status = _props->setBoolValue(path, true);
152       } else if (atof(_state.data.c_str()) != 0.0) {
153         status = _props->setBoolValue(path, true);
154       } else {
155         status =_props->setBoolValue(path, false);
156       }
157       break;
158     case SGValue::INT :
159       status = _props->setIntValue(path, atoi(_state.data.c_str()));
160       break;
161     case SGValue::FLOAT:
162       status = _props->setFloatValue(path, atof(_state.data.c_str()));
163       break;
164     case SGValue::DOUBLE:
165       status = _props->setDoubleValue(path, atof(_state.data.c_str()));
166       break;
167     case SGValue::STRING:
168       status = _props->setStringValue(path, _state.data);
169       break;
170     default:
171       status = _props->setUnknownValue(path, _state.data);
172       break;
173     }
174     if (!status)
175       FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
176              << path << " to " << _state.data);
177   }
178
179                                 // Pop the stack.
180   popState();
181 }
182
183 void
184 PropVisitor::data (const char * s, int length)
185 {
186   if (!_ok)
187     return;
188
189                                 // Check if there is any non-whitespace
190   if (!_state.hasData)
191     for (int i = 0; i < length; i++) 
192       if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r')
193         _state.hasData = true;
194
195   _state.data += string(s, length); // FIXME: inefficient
196 }
197
198 void
199 PropVisitor::warning (const char * message, int line, int col)
200 {
201   FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
202          << message << " (" << line << ',' << col << ')');
203 }
204
205 void
206 PropVisitor::error (const char * message, int line, int col)
207 {
208   FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
209          << message << " (" << line << ',' << col << ')');
210   _ok = false;
211 }
212
213
214 \f
215 ////////////////////////////////////////////////////////////////////////
216 // Property list reader.
217 ////////////////////////////////////////////////////////////////////////
218
219 bool
220 readPropertyList (istream &input, SGPropertyList * props)
221 {
222   PropVisitor visitor(props);
223   return readXML(input, visitor) && visitor.isOK();
224 }
225
226 bool
227 readPropertyList (const string &file, SGPropertyList * props)
228 {
229   ifstream input(file.c_str());
230   if (input.good()) {
231     return readPropertyList(input, props);
232   } else {
233     FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
234            << file);
235     return false;
236   }
237 }
238
239
240 \f
241 ////////////////////////////////////////////////////////////////////////
242 // Property list writer.
243 ////////////////////////////////////////////////////////////////////////
244
245 #define INDENT_STEP 2
246
247 /**
248  * Return the type name.
249  */
250 static const char *
251 getTypeName (SGValue::Type type)
252 {
253   switch (type) {
254   case SGValue::UNKNOWN:
255     return "unknown";
256   case SGValue::BOOL:
257     return "bool";
258   case SGValue::INT:
259     return "int";
260   case SGValue::FLOAT:
261     return "float";
262   case SGValue::DOUBLE:
263     return "double";
264   case SGValue::STRING:
265     return "string";
266   }
267 }
268
269
270 /**
271  * Escape characters for output.
272  */
273 static void
274 writeData (ostream &output, const string &data)
275 {
276   for (int i = 0; i < data.size(); i++) {
277     switch (data[i]) {
278     case '&':
279       output << "&amp;";
280       break;
281     case '<':
282       output << "&lt;";
283       break;
284     case '>':
285       output << "&gt;";
286       break;
287     default:
288       output << data[i];
289       break;
290     }
291   }
292 }
293
294 static void
295 doIndent (ostream &output, int indent)
296 {
297   while (indent-- > 0) {
298     output << ' ';
299   }
300 }
301
302
303 static bool
304 writeNode (ostream &output, SGPropertyNode node, int indent)
305 {
306   const string &name = node.getName();
307   int size = node.size();
308
309                                 // Write out the literal value, if any.
310   SGValue * value = node.getValue();
311   if (value != 0) {
312     SGValue::Type type = value->getType();
313     doIndent(output, indent);
314     output << '<' << name;
315     if (type != SGValue::UNKNOWN)
316       output << " type=\"" << getTypeName(type) << '"';
317     output << '>';
318     writeData(output, value->getStringValue());
319     output << "</" << name << '>' << endl;
320   }
321
322                                 // Write out the children, if any.
323   if (size > 0) {
324     doIndent(output, indent);
325     output << '<' << name << '>' << endl;;
326     for (int i = 0; i < size; i++) {
327       writeNode(output, node.getChild(i), indent + INDENT_STEP);
328     }
329     doIndent(output, indent);
330     output << "</" << name << '>' << endl;
331   }
332 }
333
334 bool
335 writePropertyList (ostream &output, const SGPropertyList * props)
336 {
337   SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
338
339   output << "<?xml version=\"1.0\"?>" << endl << endl;
340   output << "<PropertyList>" << endl;
341
342   for (int i = 0; i < root.size(); i++) {
343     writeNode(output, root.getChild(i), INDENT_STEP);
344   }
345
346   output << "</PropertyList>" << endl;
347 }
348
349 bool
350 writePropertyList (const string &file, const SGPropertyList * props)
351 {
352   ofstream output(file.c_str());
353   if (output.good()) {
354     return writePropertyList(output, props);
355   } else {
356     FG_LOG(FG_INPUT, FG_ALERT, "Cannot write property list to file "
357            << file);
358     return false;
359   }
360 }