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