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