6 #include <simgear/debug/logstream.hxx>
7 #include <simgear/xml/easyxml.hxx>
23 ////////////////////////////////////////////////////////////////////////
24 // Visitor class for building the property list.
25 ////////////////////////////////////////////////////////////////////////
27 class PropVisitor : public XMLVisitor
30 PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {}
31 void startDocument ();
32 void startElement (const char * name, const XMLAttributes &atts);
33 void endElement (const char * name);
34 void data (const char * s, int length);
35 void warning (const char * message, int line, int col);
36 void error (const char * message, int line, int col);
38 bool isOK () const { return _ok; }
42 void pushState (const char * name) {
43 _states.push_back(_state);
46 _state.type = SGValue::UNKNOWN;
48 _state.hasChildren = false;
49 _state.hasData = false;
53 _state = _states.back();
60 State () : hasChildren(false), hasData(false) {}
68 SGPropertyList * _props;
70 vector<State> _states;
76 PropVisitor::startDocument ()
84 PropVisitor::startElement (const char * name, const XMLAttributes &atts)
89 if (_level == 0 && strcmp(name, "PropertyList")) {
91 FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \""
92 << name << "\" instead of \"PropertyList\"");
97 _state.hasChildren = true;
99 FG_LOG(FG_INPUT, FG_ALERT,
100 "XML element has mixed elements and data in element "
106 // Start a new state.
109 // See if there's a type specified.
110 const char * type = atts.getValue("type");
111 if (type == 0 || !strcmp("unknown", type))
112 _state.type = SGValue::UNKNOWN;
113 else if (!strcmp("bool", type))
114 _state.type = SGValue::BOOL;
115 else if (!strcmp("int", type))
116 _state.type = SGValue::INT;
117 else if (!strcmp("float", type))
118 _state.type = SGValue::FLOAT;
119 else if (!strcmp("double", type))
120 _state.type = SGValue::DOUBLE;
121 else if (!strcmp("string", type))
122 _state.type = SGValue::STRING;
124 FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type
125 << ", using UNKNOWN");
129 PropVisitor::endElement (const char * name)
134 // See if there's a property to add.
135 if (_state.hasData) {
138 // Figure out the path name.
140 for (int i = 2; i < _level; i++) {
142 path += _states[i].name;
148 switch (_state.type) {
150 if (_state.data == "true" || _state.data == "TRUE") {
151 status = _props->setBoolValue(path, true);
152 } else if (atof(_state.data.c_str()) != 0.0) {
153 status = _props->setBoolValue(path, true);
155 status =_props->setBoolValue(path, false);
159 status = _props->setIntValue(path, atoi(_state.data.c_str()));
162 status = _props->setFloatValue(path, atof(_state.data.c_str()));
164 case SGValue::DOUBLE:
165 status = _props->setDoubleValue(path, atof(_state.data.c_str()));
167 case SGValue::STRING:
168 status = _props->setStringValue(path, _state.data);
171 status = _props->setUnknownValue(path, _state.data);
175 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
176 << path << " to " << _state.data);
184 PropVisitor::data (const char * s, int length)
189 // Check if there is any non-whitespace
191 for (int i = 0; i < length; i++)
192 if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r')
193 _state.hasData = true;
195 _state.data += string(s, length); // FIXME: inefficient
199 PropVisitor::warning (const char * message, int line, int col)
201 FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
202 << message << " (" << line << ',' << col << ')');
206 PropVisitor::error (const char * message, int line, int col)
208 FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
209 << message << " (" << line << ',' << col << ')');
215 ////////////////////////////////////////////////////////////////////////
216 // Property list reader.
217 ////////////////////////////////////////////////////////////////////////
220 readPropertyList (istream &input, SGPropertyList * props)
222 PropVisitor visitor(props);
223 return readXML(input, visitor) && visitor.isOK();
227 readPropertyList (const string &file, SGPropertyList * props)
229 ifstream input(file.c_str());
231 return readPropertyList(input, props);
233 FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
241 ////////////////////////////////////////////////////////////////////////
242 // Property list writer.
243 ////////////////////////////////////////////////////////////////////////
245 #define INDENT_STEP 2
248 * Return the type name.
251 getTypeName (SGValue::Type type)
254 case SGValue::UNKNOWN:
262 case SGValue::DOUBLE:
264 case SGValue::STRING:
271 * Escape characters for output.
274 writeData (ostream &output, const string &data)
276 for (int i = 0; i < data.size(); i++) {
295 doIndent (ostream &output, int indent)
297 while (indent-- > 0) {
304 writeNode (ostream &output, SGPropertyNode node, int indent)
306 const string &name = node.getName();
307 int size = node.size();
309 // Write out the literal value, if any.
310 SGValue * value = node.getValue();
312 SGValue::Type type = value->getType();
313 doIndent(output, indent);
314 output << '<' << name;
315 if (type != SGValue::UNKNOWN)
316 output << " type=\"" << getTypeName(type) << '"';
318 writeData(output, value->getStringValue());
319 output << "</" << name << '>' << endl;
322 // Write out the children, if any.
324 doIndent(output, indent);
325 output << '<' << name << '>' << endl;;
326 for (int i = 0; i < size; i++) {
327 writeNode(output, node.getChild(i), indent + INDENT_STEP);
329 doIndent(output, indent);
330 output << "</" << name << '>' << endl;
335 writePropertyList (ostream &output, const SGPropertyList * props)
337 SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
339 output << "<?xml version=\"1.0\"?>" << endl << endl;
340 output << "<PropertyList>" << endl;
342 for (int i = 0; i < root.size(); i++) {
343 writeNode(output, root.getChild(i), INDENT_STEP);
346 output << "</PropertyList>" << endl;
350 writePropertyList (const string &file, const SGPropertyList * props)
352 ofstream output(file.c_str());
354 return writePropertyList(output, props);
356 FG_LOG(FG_INPUT, FG_ALERT, "Cannot write property list to file "