6 #include <string.h> // strcmp()
7 #include <stdlib.h> // atof() atoi()
9 #include <simgear/debug/logstream.hxx>
10 #include <simgear/xml/easyxml.hxx>
19 FG_USING_STD(ofstream);
20 FG_USING_STD(ifstream);
26 ////////////////////////////////////////////////////////////////////////
27 // Visitor class for building the property list.
28 ////////////////////////////////////////////////////////////////////////
30 class PropVisitor : public XMLVisitor
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);
41 bool isOK () const { return _ok; }
45 void pushState (const char * name) {
46 _states.push_back(_state);
49 _state.type = SGValue::UNKNOWN;
51 _state.hasChildren = false;
52 _state.hasData = false;
56 _state = _states.back();
63 State () : hasChildren(false), hasData(false) {}
71 SGPropertyList * _props;
73 vector<State> _states;
79 PropVisitor::startDocument ()
87 PropVisitor::startElement (const char * name, const XMLAttributes &atts)
92 if (_level == 0 && strcmp(name, "PropertyList")) {
94 FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \""
95 << name << "\" instead of \"PropertyList\"");
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 "
109 // Start a new state.
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;
127 FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type
128 << ", using UNKNOWN");
132 PropVisitor::endElement (const char * name)
137 // See if there's a property to add.
138 if (_state.hasData) {
141 // Figure out the path name.
143 for (int i = 2; i < _level; i++) {
145 path += _states[i].name;
151 switch (_state.type) {
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);
158 status =_props->setBoolValue(path, false);
162 status = _props->setIntValue(path, atoi(_state.data.c_str()));
165 status = _props->setFloatValue(path, atof(_state.data.c_str()));
167 case SGValue::DOUBLE:
168 status = _props->setDoubleValue(path, atof(_state.data.c_str()));
170 case SGValue::STRING:
171 status = _props->setStringValue(path, _state.data);
174 status = _props->setUnknownValue(path, _state.data);
178 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
179 << path << " to " << _state.data);
187 PropVisitor::data (const char * s, int length)
192 // Check if there is any non-whitespace
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;
198 _state.data += string(s, length); // FIXME: inefficient
202 PropVisitor::warning (const char * message, int line, int col)
204 FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
205 << message << " (" << line << ',' << col << ')');
209 PropVisitor::error (const char * message, int line, int col)
211 FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
212 << message << " (" << line << ',' << col << ')');
218 ////////////////////////////////////////////////////////////////////////
219 // Property list reader.
220 ////////////////////////////////////////////////////////////////////////
223 readPropertyList (istream &input, SGPropertyList * props)
225 PropVisitor visitor(props);
226 return readXML(input, visitor) && visitor.isOK();
230 readPropertyList (const string &file, SGPropertyList * props)
232 ifstream input(file.c_str());
234 return readPropertyList(input, props);
236 FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
244 ////////////////////////////////////////////////////////////////////////
245 // Property list writer.
246 ////////////////////////////////////////////////////////////////////////
248 #define INDENT_STEP 2
251 * Return the type name.
254 getTypeName (SGValue::Type type)
257 case SGValue::UNKNOWN:
265 case SGValue::DOUBLE:
267 case SGValue::STRING:
271 return "unknown"; // avoid a compiler warning
276 * Escape characters for output.
279 writeData (ostream &output, const string &data)
281 for (int i = 0; i < (int)data.size(); i++) {
300 doIndent (ostream &output, int indent)
302 while (indent-- > 0) {
309 writeNode (ostream &output, SGPropertyNode node, int indent)
311 const string &name = node.getName();
312 int size = node.size();
314 // Write out the literal value, if any.
315 SGValue * value = node.getValue();
317 SGValue::Type type = value->getType();
318 doIndent(output, indent);
319 output << '<' << name;
320 if (type != SGValue::UNKNOWN)
321 output << " type=\"" << getTypeName(type) << '"';
323 writeData(output, value->getStringValue());
324 output << "</" << name << '>' << endl;
327 // Write out the children, if any.
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);
334 doIndent(output, indent);
335 output << "</" << name << '>' << endl;
342 writePropertyList (ostream &output, const SGPropertyList * props)
344 SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
346 output << "<?xml version=\"1.0\"?>" << endl << endl;
347 output << "<PropertyList>" << endl;
349 for (int i = 0; i < root.size(); i++) {
350 writeNode(output, root.getChild(i), INDENT_STEP);
353 output << "</PropertyList>" << endl;
359 writePropertyList (const string &file, const SGPropertyList * props)
361 ofstream output(file.c_str());
363 return writePropertyList(output, props);
365 FG_LOG(FG_INPUT, FG_ALERT, "Cannot write property list to file "