6 #include <simgear/debug/logstream.hxx>
7 #include <simgear/xml/easyxml.hxx>
24 ////////////////////////////////////////////////////////////////////////
25 // Visitor class for building the property list.
26 ////////////////////////////////////////////////////////////////////////
28 class PropVisitor : public XMLVisitor
31 PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {}
32 void startDocument ();
33 void startElement (const char * name, const XMLAttributes &atts);
34 void endElement (const char * name);
35 void data (const char * s, int length);
36 void warning (const char * message, int line, int col);
37 void error (const char * message, int line, int col);
39 bool isOK () const { return _ok; }
43 void pushState (const char * name) {
44 _states.push_back(_state);
47 _state.type = SGValue::UNKNOWN;
49 _state.hasChildren = false;
50 _state.hasData = false;
54 _state = _states.back();
61 State () : hasChildren(false), hasData(false) {}
69 SGPropertyList * _props;
71 vector<State> _states;
77 PropVisitor::startDocument ()
85 PropVisitor::startElement (const char * name, const XMLAttributes &atts)
90 if (_level == 0 && strcmp(name, "PropertyList")) {
92 FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \""
93 << name << "\" instead of \"PropertyList\"");
98 _state.hasChildren = true;
100 FG_LOG(FG_INPUT, FG_ALERT,
101 "XML element has mixed elements and data in element "
107 // Start a new state.
110 // See if there's a type specified.
111 const char * type = atts.getValue("type");
112 if (type == 0 || !strcmp("unknown", type))
113 _state.type = SGValue::UNKNOWN;
114 else if (!strcmp("bool", type))
115 _state.type = SGValue::BOOL;
116 else if (!strcmp("int", type))
117 _state.type = SGValue::INT;
118 else if (!strcmp("float", type))
119 _state.type = SGValue::FLOAT;
120 else if (!strcmp("double", type))
121 _state.type = SGValue::DOUBLE;
122 else if (!strcmp("string", type))
123 _state.type = SGValue::STRING;
125 FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type
126 << ", using UNKNOWN");
130 PropVisitor::endElement (const char * name)
135 // See if there's a property to add.
136 if (_state.hasData) {
139 // Figure out the path name.
141 for (int i = 2; i < _level; i++) {
143 path += _states[i].name;
149 switch (_state.type) {
151 if (_state.data == "true" || _state.data == "TRUE") {
152 status = _props->setBoolValue(path, true);
153 } else if (atof(_state.data.c_str()) != 0.0) {
154 status = _props->setBoolValue(path, true);
156 status =_props->setBoolValue(path, false);
160 status = _props->setIntValue(path, atoi(_state.data.c_str()));
163 status = _props->setFloatValue(path, atof(_state.data.c_str()));
165 case SGValue::DOUBLE:
166 status = _props->setDoubleValue(path, atof(_state.data.c_str()));
168 case SGValue::STRING:
169 status = _props->setStringValue(path, _state.data);
172 status = _props->setUnknownValue(path, _state.data);
176 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
177 << path << " to " << _state.data);
185 PropVisitor::data (const char * s, int length)
190 // Check if there is any non-whitespace
192 for (int i = 0; i < length; i++)
193 if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r')
194 _state.hasData = true;
196 _state.data += string(s, length); // FIXME: inefficient
200 PropVisitor::warning (const char * message, int line, int col)
202 FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
203 << message << " (" << line << ',' << col << ')');
207 PropVisitor::error (const char * message, int line, int col)
209 FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
210 << message << " (" << line << ',' << col << ')');
216 ////////////////////////////////////////////////////////////////////////
217 // Property list reader.
218 ////////////////////////////////////////////////////////////////////////
221 readPropertyList (istream &input, SGPropertyList * props)
223 PropVisitor visitor(props);
224 return readXML(input, visitor) && visitor.isOK();
228 readPropertyList (const string &file, SGPropertyList * props)
230 ifstream input(file.c_str());
232 return readPropertyList(input, props);
234 FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
242 ////////////////////////////////////////////////////////////////////////
243 // Property list writer.
244 ////////////////////////////////////////////////////////////////////////
246 #define INDENT_STEP 2
249 * Return the type name.
252 getTypeName (SGValue::Type type)
255 case SGValue::UNKNOWN:
263 case SGValue::DOUBLE:
265 case SGValue::STRING:
272 * Escape characters for output.
275 writeData (ostream &output, const string &data)
277 for (int i = 0; i < data.size(); i++) {
296 doIndent (ostream &output, int indent)
298 while (indent-- > 0) {
305 writeNode (ostream &output, SGPropertyNode node, int indent)
307 const string &name = node.getName();
308 int size = node.size();
310 // Write out the literal value, if any.
311 SGValue * value = node.getValue();
313 SGValue::Type type = value->getType();
314 doIndent(output, indent);
315 output << '<' << name;
316 if (type != SGValue::UNKNOWN)
317 output << " type=\"" << getTypeName(type) << '"';
319 writeData(output, value->getStringValue());
320 output << "</" << name << '>' << endl;
323 // Write out the children, if any.
325 doIndent(output, indent);
326 output << '<' << name << '>' << endl;;
327 for (int i = 0; i < size; i++) {
328 writeNode(output, node.getChild(i), indent + INDENT_STEP);
330 doIndent(output, indent);
331 output << "</" << name << '>' << endl;
338 writePropertyList (ostream &output, const SGPropertyList * props)
340 SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
342 output << "<?xml version=\"1.0\"?>" << endl << endl;
343 output << "<PropertyList>" << endl;
345 for (int i = 0; i < root.size(); i++) {
346 writeNode(output, root.getChild(i), INDENT_STEP);
349 output << "</PropertyList>" << endl;
355 writePropertyList (const string &file, const SGPropertyList * props)
357 ofstream output(file.c_str());
359 return writePropertyList(output, props);
361 FG_LOG(FG_INPUT, FG_ALERT, "Cannot write property list to file "