6 #include <simgear/debug/logstream.hxx>
7 #include <simgear/xml/easyxml.hxx>
21 ////////////////////////////////////////////////////////////////////////
22 // Visitor class for building the property list.
23 ////////////////////////////////////////////////////////////////////////
25 class PropVisitor : public XMLVisitor
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);
36 bool isOK () const { return _ok; }
40 void pushState (const char * name) {
41 _states.push_back(_state);
44 _state.type = SGValue::UNKNOWN;
46 _state.hasChildren = false;
47 _state.hasData = false;
51 _state = _states.back();
58 State () : hasChildren(false), hasData(false) {}
66 SGPropertyList * _props;
68 vector<State> _states;
74 PropVisitor::startDocument ()
82 PropVisitor::startElement (const char * name, const XMLAttributes &atts)
87 if (_level == 0 && strcmp(name, "PropertyList")) {
89 FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \""
90 << name << "\" instead of \"PropertyList\"");
95 _state.hasChildren = true;
97 FG_LOG(FG_INPUT, FG_ALERT,
98 "XML element has mixed elements and data in element "
104 // Start a new state.
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;
122 FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type
123 << ", using UNKNOWN");
127 PropVisitor::endElement (const char * name)
132 // See if there's a property to add.
133 if (_state.hasData) {
136 // Figure out the path name.
138 for (int i = 2; i < _level; i++) {
140 path += _states[i].name;
146 switch (_state.type) {
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);
153 status =_props->setBoolValue(path, false);
157 status = _props->setIntValue(path, atoi(_state.data.c_str()));
160 status = _props->setFloatValue(path, atof(_state.data.c_str()));
162 case SGValue::DOUBLE:
163 status = _props->setDoubleValue(path, atof(_state.data.c_str()));
165 case SGValue::STRING:
166 status = _props->setStringValue(path, _state.data);
169 status = _props->setUnknownValue(path, _state.data);
173 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
174 << path << " to " << _state.data);
182 PropVisitor::data (const char * s, int length)
187 // Check if there is any non-whitespace
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;
193 _state.data += string(s, length); // FIXME: inefficient
197 PropVisitor::warning (const char * message, int line, int col)
199 FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
200 << message << " (" << line << ',' << col << ')');
204 PropVisitor::error (const char * message, int line, int col)
206 FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
207 << message << " (" << line << ',' << col << ')');
213 ////////////////////////////////////////////////////////////////////////
214 // Property list reader.
215 ////////////////////////////////////////////////////////////////////////
218 readPropertyList (istream &input, SGPropertyList * props)
220 PropVisitor visitor(props);
221 return readXML(input, visitor) && visitor.isOK();
226 ////////////////////////////////////////////////////////////////////////
227 // Property list writer.
228 ////////////////////////////////////////////////////////////////////////
230 #define INDENT_STEP 2
233 * Return the type name.
236 getTypeName (SGValue::Type type)
239 case SGValue::UNKNOWN:
247 case SGValue::DOUBLE:
249 case SGValue::STRING:
256 * Escape characters for output.
259 writeData (ostream &output, const string &data)
261 for (int i = 0; i < data.size(); i++) {
280 doIndent (ostream &output, int indent)
282 while (indent-- > 0) {
289 writeNode (ostream &output, SGPropertyNode node, int indent)
291 const string &name = node.getName();
292 int size = node.size();
294 // Write out the literal value, if any.
295 SGValue * value = node.getValue();
297 SGValue::Type type = value->getType();
298 doIndent(output, indent);
299 output << '<' << name;
300 if (type != SGValue::UNKNOWN)
301 output << " type=\"" << getTypeName(type) << '"';
303 writeData(output, value->getStringValue());
304 output << "</" << name << '>' << endl;
307 // Write out the children, if any.
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);
314 doIndent(output, indent);
315 output << "</" << name << '>' << endl;
320 writePropertyList (ostream &output, const SGPropertyList * props)
322 SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
324 output << "<?xml version=\"1.0\"?>" << endl << endl;
325 output << "<PropertyList>" << endl;
327 for (int i = 0; i < root.size(); i++) {
328 writeNode(output, root.getChild(i), INDENT_STEP);
331 output << "</PropertyList>" << endl;