]> git.mxchange.org Git - simgear.git/commitdiff
From David Megginson:
authorcurt <curt>
Wed, 21 Mar 2001 04:57:13 +0000 (04:57 +0000)
committercurt <curt>
Wed, 21 Mar 2001 04:57:13 +0000 (04:57 +0000)
1. Fixed a bug reported by Curt for one of the getChild methods.

2. Cleaned up some of the error reporting, and require PropertyList as
the root element name.  Also uncluttered the XML output a little
(removed type="unknown").

3. Added a new Long type (i.e. setLongValue, getLongValue, etc.), as
requested by Curt aeons ago.

4. Added aliases, so that a property's value can directly reflect the
value of another property.

5. Added a generalized inclusion facility, with parameters (panel
writers should love this one).

simgear/misc/fgpath.cxx
simgear/misc/fgpath.hxx
simgear/misc/props.cxx
simgear/misc/props.hxx
simgear/misc/props_io.cxx
simgear/misc/props_test.cxx
simgear/xml/easyxml.cxx

index 0ae86cd81a08cb07a51b5c56322a2a9d32aa4046..e6317a3c77b60ec6cbc20bb265236250576db4aa 100644 (file)
@@ -100,3 +100,14 @@ void FGPath::concat( const string p ) {
        path += part;
     }
 }
+
+
+// get the directory part of the path.
+string FGPath::dir() {
+    int index = path.rfind(FG_PATH_SEP);
+    if (index >= 0) {
+       return path.substr(0, index);
+    } else {
+       return "";
+    }
+}
index 30f6376db182f86c93666331c5f7cd419c21a087..d3b861120ee1953c377b228aee416cb527f67cfc 100644 (file)
@@ -75,6 +75,9 @@ public:
     // path separator
     void concat( const string p );
 
+    // get the directory part of the path
+    string dir();
+  
     // get the path string
     inline string str() const { return path; }
     inline const char *c_str() { return path.c_str(); }
index 8405748cb8c1b906ced70822c4a1be128c1a9a24..7c07c203bc519c1eea200898ffa61cc5979124d6 100644 (file)
 #include <algorithm>
 #include "props.hxx"
 
-#if !defined(FG_HAVE_NATIVE_SGI_COMPILERS)
-FG_USING_STD(cerr);
-FG_USING_STD(endl);
-#endif
 FG_USING_STD(sort);
 
 
+\f
+////////////////////////////////////////////////////////////////////////
+// Local classes.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Comparator class for sorting by index.
+ */
+class CompareIndices
+{
+public:
+  int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const {
+    return (n1->getIndex() < n2->getIndex());
+  }
+};
+
+
 \f
 ////////////////////////////////////////////////////////////////////////
 // Convenience macros for value access.
@@ -32,12 +45,14 @@ FG_USING_STD(sort);
 
 #define GET_BOOL (_value.bool_val->getValue())
 #define GET_INT (_value.int_val->getValue())
+#define GET_LONG (_value.long_val->getValue())
 #define GET_FLOAT (_value.float_val->getValue())
 #define GET_DOUBLE (_value.double_val->getValue())
 #define GET_STRING (_value.string_val->getValue())
 
 #define SET_BOOL(val) (_value.bool_val->setValue(val))
 #define SET_INT(val) (_value.int_val->setValue(val))
+#define SET_LONG(val) (_value.long_val->setValue(val))
 #define SET_FLOAT(val) (_value.float_val->setValue(val))
 #define SET_DOUBLE(val) (_value.double_val->setValue(val))
 #define SET_STRING(val) (_value.string_val->setValue(val))
@@ -50,6 +65,7 @@ FG_USING_STD(sort);
 
 const bool SGRawValue<bool>::DefaultValue = false;
 const int SGRawValue<int>::DefaultValue = 0;
+const long SGRawValue<long>::DefaultValue = 0L;
 const float SGRawValue<float>::DefaultValue = 0.0;
 const double SGRawValue<double>::DefaultValue = 0.0L;
 const string SGRawValue<string>::DefaultValue = "";
@@ -227,22 +243,27 @@ find_node (SGPropertyNode * current,
           int position,
           bool create)
 {
+                               // Run off the end of the list
   if (current == 0) {
     return 0;
   }
 
+                               // Success! This is the one we want.
   else if (position >= components.size()) {
     return current;
   }
 
+                               // Empty component means root.
   else if (components[position].name == "") {
     return find_node(current->getRootNode(), components, position + 1, create);
   }
 
+                               // . means current directory
   else if (components[position].name == ".") {
     return find_node(current, components, position + 1, create);
   }
 
+                               // .. means parent directory
   else if (components[position].name == "..") {
     SGPropertyNode * parent = current->getParent();
     if (parent == 0)
@@ -251,6 +272,7 @@ find_node (SGPropertyNode * current,
       return find_node(parent, components, position + 1, create);
   }
 
+                               // Otherwise, a child name
   else {
     SGPropertyNode * child =
       current->getChild(components[position].name,
@@ -287,12 +309,19 @@ SGValue::SGValue (const SGValue &source)
   _type = source._type;
   _tied = source._tied;
   switch (source._type) {
+  case ALIAS:
+                               // FIXME!!!
+    _value.alias = (SGValue *)(source.getAlias());
+    break;
   case BOOL:
     _value.bool_val = source._value.bool_val->clone();
     break;
   case INT:
     _value.int_val = source._value.int_val->clone();
     break;
+  case LONG:
+    _value.long_val = source._value.long_val->clone();
+    break;
   case FLOAT:
     _value.float_val = source._value.float_val->clone();
     break;
@@ -312,7 +341,8 @@ SGValue::SGValue (const SGValue &source)
  */
 SGValue::~SGValue ()
 {
-  clear_value();
+  if (_type != ALIAS)
+    clear_value();
 }
 
 
@@ -323,6 +353,8 @@ void
 SGValue::clear_value ()
 {
   switch (_type) {
+  case ALIAS:
+    _value.alias->clear_value();
   case BOOL:
     delete _value.bool_val;
     _value.bool_val = 0;
@@ -331,6 +363,10 @@ SGValue::clear_value ()
     delete _value.int_val;
     _value.int_val = 0;
     break;
+  case LONG:
+    delete _value.long_val;
+    _value.long_val = 0L;
+    break;
   case FLOAT:
     delete _value.float_val;
     _value.float_val = 0;
@@ -348,6 +384,72 @@ SGValue::clear_value ()
 }
 
 
+/**
+ * Get the current type.
+ *
+ * Does not return a type of ALIAS.
+ */
+SGValue::Type
+SGValue::getType () const
+{
+  if (_type == ALIAS)
+    return _value.alias->getType();
+  else
+    return (Type)_type;
+}
+
+
+/**
+ * Get the current aliased value.
+ */
+SGValue *
+SGValue::getAlias ()
+{
+  return (_type == ALIAS ? _value.alias : 0);
+}
+
+
+/**
+ * Get the current aliased value.
+ */
+const SGValue *
+SGValue::getAlias () const
+{
+  return (_type == ALIAS ? _value.alias : 0);
+}
+
+
+/**
+ * Alias to another value.
+ */
+bool
+SGValue::alias (SGValue * alias)
+{
+  if (alias == 0 || _type == ALIAS || _tied)
+    return false;
+  clear_value();
+  _value.alias = alias;
+  _type = ALIAS;
+  return true;
+}
+
+
+/**
+ * Unalias from another value.
+ */
+bool
+SGValue::unalias ()
+{
+                               // FIXME: keep copy of previous value,
+                               // as with untie()
+  if (_type != ALIAS)
+    return false;
+  _value.string_val = new SGRawValueInternal<string>;
+  _type = UNKNOWN;
+  return true;
+}
+
+
 /**
  * Get a boolean value.
  */
@@ -355,10 +457,14 @@ bool
 SGValue::getBoolValue () const
 {
   switch (_type) {
+  case ALIAS:
+    return _value.alias->getBoolValue();
   case BOOL:
     return GET_BOOL;
   case INT:
     return GET_INT == 0 ? false : true;
+  case LONG:
+    return GET_LONG == 0L ? false : true;
   case FLOAT:
     return GET_FLOAT == 0.0 ? false : true;
   case DOUBLE:
@@ -377,14 +483,18 @@ int
 SGValue::getIntValue () const
 {
   switch (_type) {
+  case ALIAS:
+    return _value.alias->getIntValue();
   case BOOL:
-    return (int)GET_BOOL;
+    return int(GET_BOOL);
   case INT:
     return GET_INT;
+  case LONG:
+    return int(GET_LONG);
   case FLOAT:
-    return (int)GET_FLOAT;
+    return int(GET_FLOAT);
   case DOUBLE:
-    return (int)GET_DOUBLE;
+    return int(GET_DOUBLE);
   case STRING:
   case UNKNOWN:
     return atoi(GET_STRING.c_str());
@@ -392,6 +502,32 @@ SGValue::getIntValue () const
 }
 
 
+/**
+ * Get a long integer value.
+ */
+long
+SGValue::getLongValue () const
+{
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getLongValue();
+  case BOOL:
+    return long(GET_BOOL);
+  case INT:
+    return long(GET_INT);
+  case LONG:
+    return GET_LONG;
+  case FLOAT:
+    return long(GET_FLOAT);
+  case DOUBLE:
+    return long(GET_DOUBLE);
+  case STRING:
+  case UNKNOWN:
+    return strtol(GET_STRING.c_str(), 0, 0);
+  }
+}
+
+
 /**
  * Get a float value.
  */
@@ -399,14 +535,18 @@ float
 SGValue::getFloatValue () const
 {
   switch (_type) {
+  case ALIAS:
+    return _value.alias->getFloatValue();
   case BOOL:
-    return (float)GET_BOOL;
+    return float(GET_BOOL);
   case INT:
-    return (float)GET_INT;
+    return float(GET_INT);
+  case LONG:
+    return float(GET_LONG);
   case FLOAT:
     return GET_FLOAT;
   case DOUBLE:
-    return GET_DOUBLE;
+    return float(GET_DOUBLE);
   case STRING:
   case UNKNOWN:
     return atof(GET_STRING.c_str());
@@ -421,17 +561,21 @@ double
 SGValue::getDoubleValue () const
 {
   switch (_type) {
+  case ALIAS:
+    return _value.alias->getDoubleValue();
   case BOOL:
-    return (double)GET_BOOL;
+    return double(GET_BOOL);
   case INT:
-    return (double)GET_INT;
+    return double(GET_INT);
+  case LONG:
+    return double(GET_LONG);
   case FLOAT:
-    return (double)GET_FLOAT;
+    return double(GET_FLOAT);
   case DOUBLE:
     return GET_DOUBLE;
   case STRING:
   case UNKNOWN:
-    return (double)atof(GET_STRING.c_str());
+    return strtod(GET_STRING.c_str(), 0);
   }
 }
 
@@ -445,6 +589,8 @@ SGValue::getStringValue () const
   char buf[128];
 
   switch (_type) {
+  case ALIAS:
+    return _value.alias->getStringValue();
   case BOOL:
     if (GET_BOOL)
       return "true";
@@ -453,6 +599,9 @@ SGValue::getStringValue () const
   case INT:
     sprintf(buf, "%d", GET_INT);
     return buf;
+  case LONG:
+    sprintf(buf, "%ld", GET_LONG);
+    return buf;
   case FLOAT:
     sprintf(buf, "%f", GET_FLOAT);
     return buf;
@@ -479,14 +628,18 @@ SGValue::setBoolValue (bool value)
   }
 
   switch (_type) {
+  case ALIAS:
+    return _value.alias->setBoolValue(value);
   case BOOL:
     return SET_BOOL(value);
   case INT:
-    return SET_INT((int)value);
+    return SET_INT(int(value));
+  case LONG:
+    return SET_LONG(long(value));
   case FLOAT:
-    return SET_FLOAT((float)value);
+    return SET_FLOAT(float(value));
   case DOUBLE:
-    return SET_DOUBLE((double)value);
+    return SET_DOUBLE(double(value));
   case STRING:
     return SET_STRING(value ? "true" : "false");
   }
@@ -508,14 +661,54 @@ SGValue::setIntValue (int value)
   }
 
   switch (_type) {
+  case ALIAS:
+    return _value.alias->setIntValue(value);
   case BOOL:
     return SET_BOOL(value == 0 ? false : true);
   case INT:
     return SET_INT(value);
+  case LONG:
+    return SET_LONG(long(value));
   case FLOAT:
-    return SET_FLOAT((float)value);
+    return SET_FLOAT(float(value));
   case DOUBLE:
-    return SET_DOUBLE((double)value);
+    return SET_DOUBLE(double(value));
+  case STRING: {
+    char buf[128];
+    sprintf(buf, "%d", value);
+    return SET_STRING(buf);
+  }
+  }
+
+  return false;
+}
+
+
+/**
+ * Set a long value.
+ */
+bool
+SGValue::setLongValue (long value)
+{
+  if (_type == UNKNOWN) {
+    clear_value();
+    _value.long_val = new SGRawValueInternal<long>;
+    _type = LONG;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->setLongValue(value);
+  case BOOL:
+    return SET_BOOL(value == 0L ? false : true);
+  case INT:
+    return SET_INT(int(value));
+  case LONG:
+    return SET_LONG(value);
+  case FLOAT:
+    return SET_FLOAT(float(value));
+  case DOUBLE:
+    return SET_DOUBLE(double(value));
   case STRING: {
     char buf[128];
     sprintf(buf, "%d", value);
@@ -540,14 +733,18 @@ SGValue::setFloatValue (float value)
   }
 
   switch (_type) {
+  case ALIAS:
+    return _value.alias->setFloatValue(value);
   case BOOL:
     return SET_BOOL(value == 0.0 ? false : true);
   case INT:
-    return SET_INT((int)value);
+    return SET_INT(int(value));
+  case LONG:
+    return SET_LONG(long(value));
   case FLOAT:
     return SET_FLOAT(value);
   case DOUBLE:
-    return SET_DOUBLE((double)value);
+    return SET_DOUBLE(double(value));
   case STRING: {
     char buf[128];
     sprintf(buf, "%f", value);
@@ -572,12 +769,16 @@ SGValue::setDoubleValue (double value)
   }
 
   switch (_type) {
+  case ALIAS:
+    return _value.alias->setDoubleValue(value);
   case BOOL:
     return SET_BOOL(value == 0.0L ? false : true);
   case INT:
-    return SET_INT((int)value);
+    return SET_INT(int(value));
+  case LONG:
+    return SET_LONG(long(value));
   case FLOAT:
-    return SET_FLOAT((float)value);
+    return SET_FLOAT(float(value));
   case DOUBLE:
     return SET_DOUBLE(value);
   case STRING: {
@@ -604,14 +805,18 @@ SGValue::setStringValue (string value)
   }
 
   switch (_type) {
+  case ALIAS:
+    return _value.alias->setStringValue(value);
   case BOOL:
     return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false);
   case INT:
     return SET_INT(atoi(value.c_str()));
+  case LONG:
+    return SET_LONG(strtol(value.c_str(), 0, 0));
   case FLOAT:
     return SET_FLOAT(atof(value.c_str()));
   case DOUBLE:
-    return SET_DOUBLE((double)atof(value.c_str()));
+    return SET_DOUBLE(strtod(value.c_str(), 0));
   case STRING:
     return SET_STRING(value);
   }
@@ -627,14 +832,18 @@ bool
 SGValue::setUnknownValue (string value)
 {
   switch (_type) {
+  case ALIAS:
+    return _value.alias->setUnknownValue(value);
   case BOOL:
     return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false);
   case INT:
     return SET_INT(atoi(value.c_str()));
+  case LONG:
+    return SET_LONG(strtol(value.c_str(), 0, 0));
   case FLOAT:
     return SET_FLOAT(atof(value.c_str()));
   case DOUBLE:
-    return SET_DOUBLE((double)atof(value.c_str()));
+    return SET_DOUBLE(strtod(value.c_str(), 0));
   case STRING:
   case UNKNOWN:
     return SET_STRING(value);
@@ -650,7 +859,9 @@ SGValue::setUnknownValue (string value)
 bool
 SGValue::tie (const SGRawValue<bool> &value, bool use_default)
 {
-  if (_tied)
+  if (_type == ALIAS)
+    return _value.alias->tie(value, use_default);
+  else if (_tied)
     return false;
 
   bool old_val;
@@ -675,7 +886,9 @@ SGValue::tie (const SGRawValue<bool> &value, bool use_default)
 bool
 SGValue::tie (const SGRawValue<int> &value, bool use_default)
 {
-  if (_tied)
+  if (_type == ALIAS)
+    return _value.alias->tie(value, use_default);
+  else if (_tied)
     return false;
 
   int old_val;
@@ -694,13 +907,42 @@ SGValue::tie (const SGRawValue<int> &value, bool use_default)
 }
 
 
+/**
+ * Tie a long value.
+ */
+bool
+SGValue::tie (const SGRawValue<long> &value, bool use_default)
+{
+  if (_type == ALIAS)
+    return _value.alias->tie(value, use_default);
+  else if (_tied)
+    return false;
+
+  long old_val;
+  if (use_default)
+    old_val = getLongValue();
+
+  clear_value();
+  _type = LONG;
+  _tied = true;
+  _value.long_val = value.clone();
+
+  if (use_default)
+    setLongValue(old_val);
+
+  return true;
+}
+
+
 /**
  * Tie a float value.
  */
 bool
 SGValue::tie (const SGRawValue<float> &value, bool use_default)
 {
-  if (_tied)
+  if (_type == ALIAS)
+    return _value.alias->tie(value, use_default);
+  else if (_tied)
     return false;
 
   float old_val;
@@ -725,7 +967,9 @@ SGValue::tie (const SGRawValue<float> &value, bool use_default)
 bool
 SGValue::tie (const SGRawValue<double> &value, bool use_default)
 {
-  if (_tied)
+  if (_type == ALIAS)
+    return _value.alias->tie(value, use_default);
+  else if (_tied)
     return false;
 
   double old_val;
@@ -750,7 +994,9 @@ SGValue::tie (const SGRawValue<double> &value, bool use_default)
 bool
 SGValue::tie (const SGRawValue<string> &value, bool use_default)
 {
-  if (_tied)
+  if (_type == ALIAS)
+    return _value.alias->tie(value, use_default);
+  else if (_tied)
     return false;
 
   string old_val;
@@ -779,6 +1025,9 @@ SGValue::untie ()
     return false;
 
   switch (_type) {
+  case ALIAS: {
+    return _value.alias->untie();
+  }
   case BOOL: {
     bool val = getBoolValue();
     clear_value();
@@ -793,6 +1042,13 @@ SGValue::untie ()
     SET_INT(val);
     break;
   }
+  case LONG: {
+    long val = getLongValue();
+    clear_value();
+    _value.long_val = new SGRawValueInternal<long>;
+    SET_LONG(val);
+    break;
+  }
   case FLOAT: {
     float val = getFloatValue();
     clear_value();
@@ -827,11 +1083,29 @@ SGValue::untie ()
 ////////////////////////////////////////////////////////////////////////
 
 
+/**
+ * Utility function: given a value, find the property node.
+ */
+static SGPropertyNode *
+find_node_by_value (SGPropertyNode * start_node, const SGValue * value)
+{
+  if (start_node->getValue() == value) {
+    return start_node;
+  } else for (int i = 0; i < start_node->nChildren(); i++) {
+    SGPropertyNode * child =
+      find_node_by_value(start_node->getChild(i), value);
+    if (child != 0)
+      return child;
+  }
+  return 0;
+}
+
+
 /**
  * Default constructor: always creates a root node.
  */
 SGPropertyNode::SGPropertyNode ()
-  : _value(0), _name(""), _index(0), _parent(0)
+  : _value(0), _name(""), _index(0), _parent(0), _target(0)
 {
 }
 
@@ -841,10 +1115,14 @@ SGPropertyNode::SGPropertyNode ()
  */
 SGPropertyNode::SGPropertyNode (const string &name,
                                int index, SGPropertyNode * parent)
-  : _value(0), _name(name), _index(index), _parent(parent)
+  : _value(0), _name(name), _index(index), _parent(parent), _target(0)
 {
 }
 
+
+/**
+ * Destructor.
+ */
 SGPropertyNode::~SGPropertyNode ()
 {
   delete _value;
@@ -852,6 +1130,104 @@ SGPropertyNode::~SGPropertyNode ()
     delete _children[i];
 }
 
+
+/**
+ * Get a value, optionally creating it if not present.
+ */
+SGValue *
+SGPropertyNode::getValue (bool create)
+{
+  if (_value == 0 && create)
+    _value = new SGValue();
+  return _value;
+}
+
+
+/**
+ * Alias to another node.
+ */
+bool
+SGPropertyNode::alias (SGPropertyNode * target)
+{
+  if (_value == 0)
+    _value = new SGValue();
+  _target = target;
+  return _value->alias(target->getValue(true));
+}
+
+
+/**
+ * Alias to another node by path.
+ */
+bool
+SGPropertyNode::alias (const string &path)
+{
+  return alias(getNode(path, true));
+}
+
+
+/**
+ * Remove an alias.
+ */
+bool
+SGPropertyNode::unalias ()
+{
+  _target = 0;
+  return (_value != 0 ? _value->unalias() : false);
+}
+
+
+/**
+ * Test whether this node is aliased.
+ */
+bool
+SGPropertyNode::isAlias () const
+{
+  return (_value != 0 ? _value->isAlias() : false);
+}
+
+
+/**
+ * Get the target of an alias.
+ *
+ * This is tricky, because it is actually the value that is aliased,
+ * and someone could realias or unalias the value directly without
+ * going through the property node.  The node caches its best guess,
+ * but it may have to search the whole property tree.
+ *
+ * @return The target node for the alias, or 0 if the node is not aliased.
+ */
+SGPropertyNode *
+SGPropertyNode::getAliasTarget ()
+{
+  if (_value == 0 || !_value->isAlias()) {
+    return 0;
+  } else if (_target != 0 && _target->getValue() == _value->getAlias()) {
+    return _target;
+  } else {
+    _target = find_node_by_value(getRootNode(), _value->getAlias());
+  }
+}
+
+
+const SGPropertyNode *
+SGPropertyNode::getAliasTarget () const
+{
+  if (_value == 0 || !_value->isAlias()) {
+    return 0;
+  } else if (_target != 0 && _target->getValue() == _value->getAlias()) {
+    return _target;
+  } else {
+                               // FIXME: const cast
+    _target =
+      find_node_by_value((SGPropertyNode *)getRootNode(), _value->getAlias());
+  }
+}
+
+
+/**
+ * Get a non-const child by index.
+ */
 SGPropertyNode *
 SGPropertyNode::getChild (int position)
 {
@@ -861,6 +1237,10 @@ SGPropertyNode::getChild (int position)
     return 0;
 }
 
+
+/**
+ * Get a const child by index.
+ */
 const SGPropertyNode *
 SGPropertyNode::getChild (int position) const
 {
@@ -870,12 +1250,16 @@ SGPropertyNode::getChild (int position) const
     return 0;
 }
 
+
+/**
+ * Get a non-const child by name and index, creating if necessary.
+ */
 SGPropertyNode *
 SGPropertyNode::getChild (const string &name, int index, bool create)
 {
   int pos = find_child(name, index, _children);
   if (pos >= 0) {
-    return getChild(pos);
+    return _children[pos];
   } else if (create) {
     _children.push_back(new SGPropertyNode(name, index, this));
     return _children[_children.size()-1];
@@ -884,26 +1268,21 @@ SGPropertyNode::getChild (const string &name, int index, bool create)
   }
 }
 
+
+/**
+ * Get a const child by name and index.
+ */
 const SGPropertyNode *
 SGPropertyNode::getChild (const string &name, int index) const
 {
   int pos = find_child(name, index, _children);
   if (pos >= 0)
-    _children[_children.size()-1];
+    return _children[pos];
   else
     return 0;
 }
 
 
-class CompareIndices
-{
-public:
-  int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const {
-    return (n1->getIndex() < n2->getIndex());
-  }
-};
-
-
 /**
  * Get all children with the same name (but different indices).
  */
@@ -923,7 +1302,7 @@ SGPropertyNode::getChildren (const string &name)
 
 
 /**
- * Get all children with the same name (but different indices).
+ * Get all children const with the same name (but different indices).
  */
 vector<const SGPropertyNode *>
 SGPropertyNode::getChildren (const string &name) const
@@ -966,6 +1345,7 @@ SGPropertyNode::getType () const
     return SGValue::UNKNOWN;
 }
 
+
 bool 
 SGPropertyNode::getBoolValue () const
 {
@@ -980,6 +1360,13 @@ SGPropertyNode::getIntValue () const
          : _value->getIntValue());
 }
 
+long 
+SGPropertyNode::getLongValue () const
+{
+  return (_value == 0 ? SGRawValue<long>::DefaultValue
+         : _value->getLongValue());
+}
+
 float 
 SGPropertyNode::getFloatValue () const
 {
@@ -1017,6 +1404,14 @@ SGPropertyNode::setIntValue (int val)
   return _value->setIntValue(val);
 }
 
+bool
+SGPropertyNode::setLongValue (long val)
+{
+  if (_value == 0)
+    _value = new SGValue();
+  return _value->setLongValue(val);
+}
+
 bool
 SGPropertyNode::setFloatValue (float val)
 {
@@ -1071,6 +1466,14 @@ SGPropertyNode::tie (const SGRawValue<int> &rawValue, bool useDefault)
   return _value->tie(rawValue, useDefault);
 }
 
+bool
+SGPropertyNode::tie (const SGRawValue<long> &rawValue, bool useDefault)
+{
+  if (_value == 0)
+    _value = new SGValue();
+  return _value->tie(rawValue, useDefault);
+}
+
 bool
 SGPropertyNode::tie (const SGRawValue<float> &rawValue, bool useDefault)
 {
@@ -1163,7 +1566,7 @@ SGPropertyNode::getValue (const string &relative_path, bool create)
   SGPropertyNode * node = getNode(relative_path, create);
   if (node != 0 && !node->hasValue())
     node->setUnknownValue("");
-  return (node == 0 ? 0 : node->getValue());
+  return (node == 0 ? 0 : node->getValue(create));
 }
 
 
@@ -1213,6 +1616,18 @@ SGPropertyNode::getIntValue (const string &relative_path,
 }
 
 
+/**
+ * Get a long value for another node.
+ */
+long
+SGPropertyNode::getLongValue (const string &relative_path,
+                             long defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getLongValue());
+}
+
+
 /**
  * Get a float value for another node.
  */
@@ -1269,6 +1684,16 @@ SGPropertyNode::setIntValue (const string &relative_path, int value)
 }
 
 
+/**
+ * Set a long value for another node.
+ */
+bool
+SGPropertyNode::setLongValue (const string &relative_path, long value)
+{
+  return getNode(relative_path, true)->setLongValue(value);
+}
+
+
 /**
  * Set a float value for another node.
  */
@@ -1344,6 +1769,18 @@ SGPropertyNode::tie (const string &relative_path,
 }
 
 
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const string &relative_path,
+                    const SGRawValue<long> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
 /**
  * Tie a node reached by a relative path, creating it if necessary.
  */
index d6809970e83eab9eff0d5b23fb3b57211b020ae1..f1e23464f1ee644675d27b79dd2abf4a890a74eb 100644 (file)
@@ -28,6 +28,11 @@ FG_USING_STD(istream);
 FG_USING_STD(ostream);
 #endif
 
+#ifdef ALIAS
+#pragma warn A sloppy coder has defined ALIAS as a macro!
+#undef ALIAS
+#endif
+
 #ifdef UNKNOWN
 #pragma warn A sloppy coder has defined UNKNOWN as a macro!
 #undef UNKNOWN
@@ -43,6 +48,11 @@ FG_USING_STD(ostream);
 #undef INT
 #endif
 
+#ifdef LONG
+#pragma warn A sloppy coder has defined LONG as a macro!
+#undef LONG
+#endif
+
 #ifdef FLOAT
 #pragma warn A sloppy coder has defined FLOAT as a macro!
 #undef FLOAT
@@ -294,6 +304,7 @@ public:
   enum Type {
     BOOL,
     INT,
+    LONG,
     FLOAT,
     DOUBLE,
     STRING,
@@ -303,16 +314,24 @@ public:
   SGValue (const SGValue &value);
   ~SGValue ();
 
-  Type getType () const { return _type; }
+  Type getType () const;
+
+  SGValue * getAlias ();
+  const SGValue * getAlias () const;
+  bool alias (SGValue * alias);
+  bool unalias ();
+  bool isAlias () const { return _type == ALIAS; }
 
   bool getBoolValue () const;
   int getIntValue () const;
+  long getLongValue () const;
   float getFloatValue () const;
   double getDoubleValue () const;
   string getStringValue () const;
 
   bool setBoolValue (bool value);
   bool setIntValue (int value);
+  bool setLongValue (long value);
   bool setFloatValue (float value);
   bool setDoubleValue (double value);
   bool setStringValue (string value);
@@ -322,6 +341,7 @@ public:
 
   bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
+  bool tie (const SGRawValue<long> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<string> &rawValue, bool useDefault = true);
@@ -330,15 +350,21 @@ public:
 
 private:
 
+  enum {
+    ALIAS = -1
+  };
+
   void clear_value ();
 
-  Type _type;
+  int _type;
   bool _tied;
 
                                // The right kind of pointer...
   union {
+    SGValue * alias;
     SGRawValue<bool> * bool_val;
     SGRawValue<int> * int_val;
+    SGRawValue<long> * long_val;
     SGRawValue<float> * float_val;
     SGRawValue<double> * double_val;
     SGRawValue<string> * string_val;
@@ -358,17 +384,26 @@ class SGPropertyNode
 public:
 
   SGPropertyNode ();
-  virtual ~SGPropertyNode ();
+   virtual ~SGPropertyNode ();
 
                                // Basic properties.
   bool hasValue () const { return (_value != 0); }
   SGValue * getValue () { return _value; }
+  SGValue * getValue (bool create);
   const SGValue * getValue () const { return _value; }
   const string &getName () const { return _name; }
   const int getIndex () const { return _index; }
   SGPropertyNode * getParent () { return _parent; }
   const SGPropertyNode * getParent () const { return _parent; }
 
+                               // Alias support.
+  bool alias (SGPropertyNode * target);
+  bool alias (const string &path);
+  bool unalias ();
+  bool isAlias () const;
+  SGPropertyNode * getAliasTarget ();
+  const SGPropertyNode * getAliasTarget () const;
+
                                // Children.
   const int nChildren () const { return _children.size(); }
   SGPropertyNode * getChild (int position);
@@ -394,12 +429,14 @@ public:
   
   bool getBoolValue () const;
   int getIntValue () const;
+  long getLongValue () const;
   float getFloatValue () const;
   double getDoubleValue () const;
   string getStringValue () const;
 
   bool setBoolValue (bool value);
   bool setIntValue (int value);
+  bool setLongValue (long value);
   bool setFloatValue (float value);
   bool setDoubleValue (double value);
   bool setStringValue (string value);
@@ -409,6 +446,7 @@ public:
 
   bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
+  bool tie (const SGRawValue<long> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
   bool tie (const SGRawValue<string> &rawValue, bool useDefault = true);
@@ -426,6 +464,8 @@ public:
                     bool defaultValue = false) const;
   int getIntValue (const string &relative_path,
                   int defaultValue = 0) const;
+  long getLongValue (const string &relative_path,
+                    long defaultValue = 0L) const;
   float getFloatValue (const string &relative_path,
                       float defaultValue = 0.0) const;
   double getDoubleValue (const string &relative_path,
@@ -435,6 +475,7 @@ public:
 
   bool setBoolValue (const string &relative_path, bool value);
   bool setIntValue (const string &relative_path, int value);
+  bool setLongValue (const string &relative_path, long value);
   bool setFloatValue (const string &relative_path, float value);
   bool setDoubleValue (const string &relative_path, double value);
   bool setStringValue (const string &relative_path, string value);
@@ -446,6 +487,8 @@ public:
            bool useDefault = true);
   bool tie (const string &relative_path, const SGRawValue<int> &rawValue,
            bool useDefault = true);
+  bool tie (const string &relative_path, const SGRawValue<long> &rawValue,
+           bool useDefault = true);
   bool tie (const string &relative_path, const SGRawValue<float> &rawValue,
            bool useDefault = true);
   bool tie (const string &relative_path, const SGRawValue<double> &rawValue,
@@ -468,6 +511,7 @@ private:
   int _index;
   SGPropertyNode * _parent;
   vector<SGPropertyNode *> _children;
+  mutable SGPropertyNode * _target;
 
 };
 
@@ -477,7 +521,8 @@ private:
 // I/O functions.
 ////////////////////////////////////////////////////////////////////////
 
-bool readProperties (istream &input, SGPropertyNode * start_node);
+bool readProperties (istream &input, SGPropertyNode * start_node,
+                    const string &base = "");
 bool readProperties (const string &file, SGPropertyNode * start_node);
 bool writeProperties (ostream &output, const SGPropertyNode * start_node);
 bool writeProperties (const string &file, const SGPropertyNode * start_node);
index b9ecabfc3c1b1bb295e5b175a0a5f4d692829d66..1951c229c01b1d4cabd3d96a54f68c7d3b139859 100644 (file)
@@ -10,6 +10,7 @@
 #include <simgear/debug/logstream.hxx>
 #include <simgear/xml/easyxml.hxx>
 
+#include "fgpath.hxx"
 #include "props.hxx"
 
 #include STL_IOSTREAM
@@ -42,7 +43,8 @@ class PropsVisitor : public XMLVisitor
 {
 public:
 
-  PropsVisitor (SGPropertyNode * root) : _ok(true), _root(root), _level(0) {}
+  PropsVisitor (SGPropertyNode * root, const string &base)
+    : _ok(true), _root(root), _level(0), _base(base) {}
 
   void startXML ();
   void endXML ();
@@ -87,7 +89,7 @@ private:
   SGPropertyNode * _root;
   int _level;
   vector<State> _state_stack;
-
+  string _base;
 };
 
 void
@@ -110,10 +112,16 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
   State &st = state();
 
   if (_level == 0) {
+    if (string(name) != "PropertyList") {
+      FG_LOG(FG_INPUT, FG_ALERT, "Root element name is " <<
+            name << "; expected PropertyList");
+      _ok = false;
+    }
     push_state(_root, "");
   }
 
   else {
+                               // Get the index.
     const char * att_n = atts.getValue("n");
     int index = 0;
     if (att_n != 0) {
@@ -123,8 +131,30 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
       index = st.counters[name];
       st.counters[name]++;
     }
-    push_state(st.node->getChild(name, index, true),
-              atts.getValue("type"));
+
+                               // Check for an alias.
+    SGPropertyNode * node = st.node->getChild(name, index, true);
+    const char * att_alias = atts.getValue("alias");
+    if (att_alias != 0) {
+      if (!node->alias(att_alias))
+       FG_LOG(FG_INPUT, FG_ALERT, "Failed to set alias to " << att_alias);
+    }
+
+                               // Check for an include.
+    const char * att_include = atts.getValue("include");
+    if (att_include != 0) {
+      FGPath path(FGPath(_base).dir());
+      cerr << "Base is " << _base << endl;
+      cerr << "Dir is " << FGPath(_base).dir() << endl;
+      path.append(att_include);
+      if (!readProperties(path.str(), node)) {
+       FG_LOG(FG_INPUT, FG_ALERT, "Failed to read include file "
+              << att_include);
+       _ok = false;
+      }
+    }
+
+    push_state(node, atts.getValue("type"));
   }
 }
 
@@ -134,9 +164,9 @@ PropsVisitor::endElement (const char * name)
   State &st = state();
   bool ret;
 
-                               // If there are no children, then
-                               // it is a leaf value.
-  if (st.node->nChildren() == 0) {
+                               // If there are no children and it's
+                               // not an alias, then it's a leaf value.
+  if (st.node->nChildren() == 0 && !st.node->isAlias()) {
     if (st.type == "bool") {
       if (_data == "true" || atoi(_data.c_str()) != 0)
        ret = st.node->setBoolValue(true);
@@ -183,8 +213,8 @@ PropsVisitor::warning (const char * message, int line, int column)
 void
 PropsVisitor::error (const char * message, int line, int column)
 {
-  FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: "
-        << message << " at line " << line << ", column " << column);
+  FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: " <<
+        message << " at line " << line << ", column " << column);
   _ok = false;
 }
 
@@ -194,19 +224,38 @@ PropsVisitor::error (const char * message, int line, int column)
 // Property list reader.
 ////////////////////////////////////////////////////////////////////////
 
+
+/**
+ * Read properties from an input stream.
+ *
+ * @param input The input stream containing an XML property file.
+ * @param start_node The root node for reading properties.
+ * @param base A base path for resolving external include references.
+ * @return true if the read succeeded, false otherwise.
+ */
 bool
-readProperties (istream &input, SGPropertyNode * start_node)
+readProperties (istream &input, SGPropertyNode * start_node,
+               const string &base)
 {
-  PropsVisitor visitor(start_node);
+  PropsVisitor visitor(start_node, base);
   return readXML(input, visitor) && visitor.isOK();
 }
 
+
+/**
+ * Read properties from a file.
+ *
+ * @param file A string containing the file path.
+ * @param start_node The root node for reading properties.
+ * @return true if the read succeeded, false otherwise.
+ */
 bool
 readProperties (const string &file, SGPropertyNode * start_node)
 {
+  cerr << "Reading properties from " << file << endl;
   ifstream input(file.c_str());
   if (input.good()) {
-    return readProperties(input, start_node);
+    return readProperties(input, start_node, file);
   } else {
     FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file "
           << file);
@@ -292,10 +341,17 @@ writeNode (ostream &output, const SGPropertyNode * node, int indent)
                                // write it first.
   if (node->hasValue()) {
     doIndent(output, indent);
-    output << '<' << name << " n=\"" << index
-          << "\" type=\"" << getTypeName(node->getType()) << "\">";
-    writeData(output, node->getStringValue());
-    output << "</" << name << '>' << endl;;
+    output << '<' << name << " n=\"" << index;
+    if (node->isAlias() && node->getAliasTarget() != 0) {
+      output << "\" alias=\""
+            << node->getAliasTarget()->getPath() << "\"/>" << endl;
+    } else {
+      if (node->getType() != SGValue::UNKNOWN)
+       output << "\" type=\"" << getTypeName(node->getType()) << '"';
+      output << '>';
+      writeData(output, node->getStringValue());
+      output << "</" << name << '>' << endl;;
+    }
   }
 
                                // If there are children, write them
@@ -320,6 +376,14 @@ writeNode (ostream &output, const SGPropertyNode * node, int indent)
   return true;
 }
 
+
+/**
+ * Write a property tree to an output stream in XML format.
+ *
+ * @param output The output stream.
+ * @param start_node The root node to write.
+ * @return true if the write succeeded, false otherwise.
+ */
 bool
 writeProperties (ostream &output, const SGPropertyNode * start_node)
 {
@@ -337,6 +401,14 @@ writeProperties (ostream &output, const SGPropertyNode * start_node)
   return true;
 }
 
+
+/**
+ * Write a property tree to a file in XML format.
+ *
+ * @param file The destination file.
+ * @param start_node The root node to write.
+ * @return true if the write succeeded, false otherwise.
+ */
 bool
 writeProperties (const string &file, const SGPropertyNode * start_node)
 {
@@ -351,8 +423,19 @@ writeProperties (const string &file, const SGPropertyNode * start_node)
 }
 
 
+\f
+////////////////////////////////////////////////////////////////////////
+// Copy properties from one tree to another.
+////////////////////////////////////////////////////////////////////////
+
+
 /**
- * Copy one property list to another.
+ * Copy one property tree to another.
+ * 
+ * @param in The source property tree.
+ * @param out The destination property tree.
+ * @return true if all properties were copied, false if some failed
+ *  (for example, if the property's value is tied read-only).
  */
 bool
 copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
@@ -388,7 +471,7 @@ copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
        retval = false;
       break;
     default:
-      throw string("Unknown SGValue type"); // FIXME!!!
+      throw string("Unknown SGValue type");
     }
   }
 
index 3fa2333b1fdee3ff84f3bb52d600d54a3835ce35..9a0487ccb314f2c009f97c75ad3d7a952d434909 100644 (file)
@@ -317,7 +317,7 @@ test_property_nodes ()
   cout << "Looking for all /hack[0]/bar children" << endl;
   vector<SGPropertyNode *> bar = child->getChildren("bar");
   cout << "There are " << bar.size() << " matches" << endl;
-  for (int i = 0; i < bar.size(); i++)
+  for (int i = 0; i < (int)bar.size(); i++)
     cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl;
   cout << endl;
 
index 83e0735af2d998c9b9e77a683b9dcf52e7c0ee30..ab97d2180f8267b7df747dc09bf7b6f15605dcfd 100644 (file)
@@ -228,6 +228,10 @@ readXML (istream &input, XMLVisitor &visitor)
     }
   }
 
+                               // Verify end of document.
+  if (!XML_Parse(parser, buf, 0, true))
+    retval = false;
+
   if (retval)
     visitor.endXML();