]> git.mxchange.org Git - simgear.git/blob - simgear/misc/props_io.cxx
Fix a build order problem.
[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   }
143
144   if (!ret)
145     FG_LOG(FG_INPUT, FG_ALERT, "readProperties: Failed to set "
146            << st.node->getPath() << " to value \""
147            << _data << " with type " << st.type);
148
149   pop_state();
150 }
151
152 void
153 PropsVisitor::data (const char * s, int length)
154 {
155   if (state().node->nChildren() == 0)
156     _data.append(string(s, length));
157 }
158
159 void
160 PropsVisitor::warning (const char * message, int line, int column)
161 {
162   FG_LOG(FG_INPUT, FG_ALERT, "readProperties: warning: "
163          << message << " at line " << line << ", column " << column);
164 }
165
166 void
167 PropsVisitor::error (const char * message, int line, int column)
168 {
169   FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: "
170          << message << " at line " << line << ", column " << column);
171   _ok = false;
172 }
173
174
175 \f
176 ////////////////////////////////////////////////////////////////////////
177 // Property list reader.
178 ////////////////////////////////////////////////////////////////////////
179
180 bool
181 readProperties (istream &input, SGPropertyNode * start_node)
182 {
183   PropsVisitor visitor(start_node);
184   return readXML(input, visitor) && visitor.isOK();
185 }
186
187 bool
188 readProperties (const string &file, SGPropertyNode * start_node)
189 {
190   ifstream input(file.c_str());
191   if (input.good()) {
192     return readProperties(input, start_node);
193   } else {
194     FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
195            << file);
196     return false;
197   }
198 }
199
200
201 \f
202 ////////////////////////////////////////////////////////////////////////
203 // Property list writer.
204 ////////////////////////////////////////////////////////////////////////
205
206 #define INDENT_STEP 2
207
208 /**
209  * Return the type name.
210  */
211 static const char *
212 getTypeName (SGValue::Type type)
213 {
214   switch (type) {
215   case SGValue::UNKNOWN:
216     return "unknown";
217   case SGValue::BOOL:
218     return "bool";
219   case SGValue::INT:
220     return "int";
221   case SGValue::FLOAT:
222     return "float";
223   case SGValue::DOUBLE:
224     return "double";
225   case SGValue::STRING:
226     return "string";
227   }
228 }
229
230
231 /**
232  * Escape characters for output.
233  */
234 static void
235 writeData (ostream &output, const string &data)
236 {
237   for (int i = 0; i < data.size(); i++) {
238     switch (data[i]) {
239     case '&':
240       output << "&amp;";
241       break;
242     case '<':
243       output << "&lt;";
244       break;
245     case '>':
246       output << "&gt;";
247       break;
248     default:
249       output << data[i];
250       break;
251     }
252   }
253 }
254
255 static void
256 doIndent (ostream &output, int indent)
257 {
258   while (indent-- > 0) {
259     output << ' ';
260   }
261 }
262
263
264 static bool
265 writeNode (ostream &output, const SGPropertyNode * node, int indent)
266 {
267   const string &name = node->getName();
268   int index = node->getIndex();
269   int nChildren = node->nChildren();
270
271                                 // If there is a literal value,
272                                 // write it first.
273   if (node->hasValue()) {
274     doIndent(output, indent);
275     output << '<' << name << " n=\"" << index
276            << "\" type=\"" << getTypeName(node->getType()) << "\">";
277     writeData(output, node->getStringValue());
278     output << "</" << name << '>' << endl;;
279   }
280
281                                 // If there are children, write them
282                                 // next.
283   if (nChildren > 0) {
284     doIndent(output, indent);
285     output << '<' << name << " n=\"" << index << "\">" << endl;;
286     for (int i = 0; i < nChildren; i++)
287       writeNode(output, node->getChild(i), indent + INDENT_STEP);
288     doIndent(output, indent);
289     output << "</" << name << '>' << endl;
290   }
291
292                                 // If there were no children and no
293                                 // value, at least note the presence
294                                 // of the node.
295   if (nChildren == 0 && !node->hasValue()) {
296     doIndent(output, indent);
297     output << '<' << name << " n=\"" << index << "\"/>" << endl;
298   }
299
300   return true;
301 }
302
303 bool
304 writeProperties (ostream &output, const SGPropertyNode * start_node)
305 {
306   int nChildren = start_node->nChildren();
307
308   output << "<?xml version=\"1.0\"?>" << endl << endl;
309   output << "<PropertyList>" << endl;
310
311   for (int i = 0; i < nChildren; i++) {
312     writeNode(output, start_node->getChild(i), INDENT_STEP);
313   }
314
315   output << "</PropertyList>" << endl;
316
317   return true;
318 }
319
320 bool
321 writeProperties (const string &file, const SGPropertyNode * start_node)
322 {
323   ofstream output(file.c_str());
324   if (output.good()) {
325     return writeProperties(output, start_node);
326   } else {
327     FG_LOG(FG_INPUT, FG_ALERT, "Cannot write properties to file "
328            << file);
329     return false;
330   }
331 }