6 #include <simgear/compiler.h>
8 #include <stdlib.h> // atof() atoi()
10 #include <simgear/debug/logstream.hxx>
11 #include <simgear/xml/easyxml.hxx>
17 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
26 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
27 SG_USING_STD(istream);
28 SG_USING_STD(ifstream);
29 SG_USING_STD(ostream);
30 SG_USING_STD(ofstream);
38 ////////////////////////////////////////////////////////////////////////
39 // Property list visitor, for XML parsing.
40 ////////////////////////////////////////////////////////////////////////
42 class PropsVisitor : public XMLVisitor
46 PropsVisitor (SGPropertyNode * root, const string &base)
47 : _ok(true), _root(root), _level(0), _base(base) {}
51 void startElement (const char * name, const XMLAttributes &atts);
52 void endElement (const char * name);
53 void data (const char * s, int length);
54 void warning (const char * message, int line, int column);
55 void error (const char * message, int line, int column);
57 bool isOK () const { return _ok; }
63 State () : node(0), type("") {}
64 State (SGPropertyNode * _node, const char * _type)
65 : node(_node), type(_type) {}
66 SGPropertyNode * node;
68 map<string,int> counters;
71 State &state () { return _state_stack[_state_stack.size() - 1]; }
73 void push_state (SGPropertyNode * node, const char * type) {
75 _state_stack.push_back(State(node, "unknown"));
77 _state_stack.push_back(State(node, type));
83 _state_stack.pop_back();
89 SGPropertyNode * _root;
91 vector<State> _state_stack;
96 PropsVisitor::startXML ()
99 _state_stack.resize(0);
103 PropsVisitor::endXML ()
106 _state_stack.resize(0);
110 PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
115 if (string(name) != "PropertyList") {
116 FG_LOG(FG_INPUT, FG_ALERT, "Root element name is " <<
117 name << "; expected PropertyList");
120 push_state(_root, "");
125 const char * att_n = atts.getValue("n");
129 st.counters[name] = max(st.counters[name], index+1);
131 index = st.counters[name];
135 // Check for an alias.
136 SGPropertyNode * node = st.node->getChild(name, index, true);
137 const char * att_alias = atts.getValue("alias");
138 if (att_alias != 0) {
139 if (!node->alias(att_alias))
140 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set alias to " << att_alias);
143 // Check for an include.
144 const char * att_include = atts.getValue("include");
145 if (att_include != 0) {
146 FGPath path(FGPath(_base).dir());
147 cerr << "Base is " << _base << endl;
148 cerr << "Dir is " << FGPath(_base).dir() << endl;
149 path.append(att_include);
150 if (!readProperties(path.str(), node)) {
151 FG_LOG(FG_INPUT, FG_ALERT, "Failed to read include file "
157 push_state(node, atts.getValue("type"));
162 PropsVisitor::endElement (const char * name)
167 // If there are no children and it's
168 // not an alias, then it's a leaf value.
169 if (st.node->nChildren() == 0 && !st.node->isAlias()) {
170 if (st.type == "bool") {
171 if (_data == "true" || atoi(_data.c_str()) != 0)
172 ret = st.node->setBoolValue(true);
174 ret = st.node->setBoolValue(false);
175 } else if (st.type == "int") {
176 ret = st.node->setIntValue(atoi(_data.c_str()));
177 } else if (st.type == "float") {
178 ret = st.node->setFloatValue(atof(_data.c_str()));
179 } else if (st.type == "double") {
180 ret = st.node->setDoubleValue(atof(_data.c_str()));
181 } else if (st.type == "string") {
182 ret = st.node->setStringValue(_data);
183 } else if (st.type == "unknown") {
184 ret = st.node->setUnknownValue(_data);
186 FG_LOG(FG_INPUT, FG_ALERT, "Unknown data type " << st.type
187 << " assuming 'unknown'");
188 ret = st.node->setUnknownValue(_data);
191 FG_LOG(FG_INPUT, FG_ALERT, "readProperties: Failed to set "
192 << st.node->getPath() << " to value \""
193 << _data << "\" with type " << st.type);
200 PropsVisitor::data (const char * s, int length)
202 if (state().node->nChildren() == 0)
203 _data.append(string(s, length));
207 PropsVisitor::warning (const char * message, int line, int column)
209 FG_LOG(FG_INPUT, FG_ALERT, "readProperties: warning: "
210 << message << " at line " << line << ", column " << column);
214 PropsVisitor::error (const char * message, int line, int column)
216 FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: " <<
217 message << " at line " << line << ", column " << column);
223 ////////////////////////////////////////////////////////////////////////
224 // Property list reader.
225 ////////////////////////////////////////////////////////////////////////
229 * Read properties from an input stream.
231 * @param input The input stream containing an XML property file.
232 * @param start_node The root node for reading properties.
233 * @param base A base path for resolving external include references.
234 * @return true if the read succeeded, false otherwise.
237 readProperties (istream &input, SGPropertyNode * start_node,
240 PropsVisitor visitor(start_node, base);
241 return readXML(input, visitor) && visitor.isOK();
246 * Read properties from a file.
248 * @param file A string containing the file path.
249 * @param start_node The root node for reading properties.
250 * @return true if the read succeeded, false otherwise.
253 readProperties (const string &file, SGPropertyNode * start_node)
255 cerr << "Reading properties from " << file << endl;
256 ifstream input(file.c_str());
258 return readProperties(input, start_node, file);
260 FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
268 ////////////////////////////////////////////////////////////////////////
269 // Property list writer.
270 ////////////////////////////////////////////////////////////////////////
272 #define INDENT_STEP 2
275 * Return the type name.
278 getTypeName (SGValue::Type type)
281 case SGValue::UNKNOWN:
289 case SGValue::DOUBLE:
291 case SGValue::STRING:
295 // keep the compiler from squawking
301 * Escape characters for output.
304 writeData (ostream &output, const string &data)
306 for (int i = 0; i < (int)data.size(); i++) {
325 doIndent (ostream &output, int indent)
327 while (indent-- > 0) {
334 writeNode (ostream &output, const SGPropertyNode * node, int indent)
336 const string &name = node->getName();
337 int index = node->getIndex();
338 int nChildren = node->nChildren();
340 // If there is a literal value,
342 if (node->hasValue()) {
343 doIndent(output, indent);
344 output << '<' << name << " n=\"" << index;
345 if (node->isAlias() && node->getAliasTarget() != 0) {
346 output << "\" alias=\""
347 << node->getAliasTarget()->getPath() << "\"/>" << endl;
349 if (node->getType() != SGValue::UNKNOWN)
350 output << "\" type=\"" << getTypeName(node->getType()) << '"';
352 writeData(output, node->getStringValue());
353 output << "</" << name << '>' << endl;;
357 // If there are children, write them
360 doIndent(output, indent);
361 output << '<' << name << " n=\"" << index << "\">" << endl;;
362 for (int i = 0; i < nChildren; i++)
363 writeNode(output, node->getChild(i), indent + INDENT_STEP);
364 doIndent(output, indent);
365 output << "</" << name << '>' << endl;
368 // If there were no children and no
369 // value, at least note the presence
371 if (nChildren == 0 && !node->hasValue()) {
372 doIndent(output, indent);
373 output << '<' << name << " n=\"" << index << "\"/>" << endl;
381 * Write a property tree to an output stream in XML format.
383 * @param output The output stream.
384 * @param start_node The root node to write.
385 * @return true if the write succeeded, false otherwise.
388 writeProperties (ostream &output, const SGPropertyNode * start_node)
390 int nChildren = start_node->nChildren();
392 output << "<?xml version=\"1.0\"?>" << endl << endl;
393 output << "<PropertyList>" << endl;
395 for (int i = 0; i < nChildren; i++) {
396 writeNode(output, start_node->getChild(i), INDENT_STEP);
399 output << "</PropertyList>" << endl;
406 * Write a property tree to a file in XML format.
408 * @param file The destination file.
409 * @param start_node The root node to write.
410 * @return true if the write succeeded, false otherwise.
413 writeProperties (const string &file, const SGPropertyNode * start_node)
415 ofstream output(file.c_str());
417 return writeProperties(output, start_node);
419 FG_LOG(FG_INPUT, FG_ALERT, "Cannot write properties to file "
427 ////////////////////////////////////////////////////////////////////////
428 // Copy properties from one tree to another.
429 ////////////////////////////////////////////////////////////////////////
433 * Copy one property tree to another.
435 * @param in The source property tree.
436 * @param out The destination property tree.
437 * @return true if all properties were copied, false if some failed
438 * (for example, if the property's value is tied read-only).
441 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
445 // First, copy the actual value,
447 if (in->hasValue()) {
448 switch (in->getType()) {
450 if (!out->setBoolValue(in->getBoolValue()))
454 if (!out->setIntValue(in->getIntValue()))
458 if (!out->setFloatValue(in->getFloatValue()))
461 case SGValue::DOUBLE:
462 if (!out->setDoubleValue(in->getDoubleValue()))
465 case SGValue::STRING:
466 if (!out->setStringValue(in->getStringValue()))
469 case SGValue::UNKNOWN:
470 if (!out->setUnknownValue(in->getStringValue()))
474 throw string("Unknown SGValue type");
478 // Next, copy the children.
479 int nChildren = in->nChildren();
480 for (int i = 0; i < nChildren; i++) {
481 const SGPropertyNode * in_child = in->getChild(i);
482 SGPropertyNode * out_child = out->getChild(in_child->getName(),
483 in_child->getIndex(),
485 if (!copyProperties(in_child, out_child))
492 // end of props_io.cxx