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