]> git.mxchange.org Git - simgear.git/commitdiff
9/05/2000 updates from David Megginson relating to the property manager and
authorcurt <curt>
Tue, 5 Sep 2000 21:38:00 +0000 (21:38 +0000)
committercurt <curt>
Tue, 5 Sep 2000 21:38:00 +0000 (21:38 +0000)
easyxml support.  This is a bit of simgear side support to facilitate a
configurable instrument panel in flightgear.

simgear/misc/Makefile.am
simgear/misc/props.cxx
simgear/misc/props.hxx
simgear/misc/props_io.cxx [new file with mode: 0644]
simgear/xml/easyxml.cxx

index f2166e1fdea6e0828f6f9ec7f1ca15d1668d80aa..b11191802215a5b35e8570886d7009478e1e0067 100644 (file)
@@ -21,6 +21,7 @@ libsgmisc_a_SOURCES = \
        fgpath.cxx \
        fgstream.cxx \
        props.cxx \
+        props_io.cxx \
        strutils.cxx \
        texcoord.cxx \
        zfstream.cxx
index 60a2b1934f659adc2a8a66c7311b4199ccfe8fd6..b59191271185ea4715ad1b0c1835b80a5d940ceb 100644 (file)
@@ -304,8 +304,6 @@ float
 SGValue::getFloatValue () const
 {
   switch (_type) {
-  case UNKNOWN:
-    return 0.0;
   case BOOL:
     return (float)(getRawBool());
   case INT:
@@ -314,6 +312,7 @@ SGValue::getFloatValue () const
     return getRawFloat();
   case DOUBLE:
     return (float)(getRawDouble());
+  case UNKNOWN:
   case STRING:
     return (float)atof(getRawString().c_str());
   }
@@ -330,8 +329,6 @@ double
 SGValue::getDoubleValue () const
 {
   switch (_type) {
-  case UNKNOWN:
-    return 0.0;
   case BOOL:
     return (double)(getRawBool());
   case INT:
@@ -340,6 +337,7 @@ SGValue::getDoubleValue () const
     return (double)(getRawFloat());
   case DOUBLE:
     return getRawDouble();
+  case UNKNOWN:
   case STRING:
     return atof(getRawString().c_str());
   }
@@ -357,8 +355,6 @@ SGValue::getStringValue () const
 {
   char buf[512];
   switch (_type) {
-  case UNKNOWN:
-    return getRawString();
   case BOOL:
     if (getRawBool())
       string_val = "true";
@@ -377,6 +373,7 @@ SGValue::getStringValue () const
     sprintf(buf, "%f", getRawDouble());
     string_val = buf;
     return string_val;
+  case UNKNOWN:
   case STRING:
     return getRawString();
   }
@@ -739,11 +736,11 @@ SGPropertyList::getValue (const string &name) const
  * better to get the SGValue and query it repeatedly.
  */
 bool
-SGPropertyList::getBoolValue (const string &name) const
+SGPropertyList::getBoolValue (const string &name, bool defaultValue) const
 {
   const SGValue * val = getValue(name);
   if (val == 0)
-    return false;
+    return defaultValue;
   else
     return val->getBoolValue();
 }
@@ -756,11 +753,11 @@ SGPropertyList::getBoolValue (const string &name) const
  * better to get the SGValue and query it repeatedly.
  */
 int
-SGPropertyList::getIntValue (const string &name) const
+SGPropertyList::getIntValue (const string &name, int defaultValue) const
 {
   const SGValue * val = getValue(name);
   if (val == 0)
-    return 0;
+    return defaultValue;
   else
     return val->getIntValue();
 }
@@ -773,11 +770,11 @@ SGPropertyList::getIntValue (const string &name) const
  * better to get the SGValue and query it repeatedly.
  */
 float
-SGPropertyList::getFloatValue (const string &name) const
+SGPropertyList::getFloatValue (const string &name, float defaultValue) const
 {
   const SGValue * val = getValue(name);
   if (val == 0)
-    return 0.0;
+    return defaultValue;
   else
     return val->getFloatValue();
 }
@@ -790,11 +787,11 @@ SGPropertyList::getFloatValue (const string &name) const
  * better to get the SGValue and query it repeatedly.
  */
 double
-SGPropertyList::getDoubleValue (const string &name) const
+SGPropertyList::getDoubleValue (const string &name, double defaultValue) const
 {
   const SGValue * val = getValue(name);
   if (val == 0)
-    return 0.0;
+    return defaultValue;
   else
     return val->getDoubleValue();
 }
@@ -807,11 +804,12 @@ SGPropertyList::getDoubleValue (const string &name) const
  * better to save the SGValue and query it repeatedly.
  */
 const string &
-SGPropertyList::getStringValue (const string &name) const
+SGPropertyList::getStringValue (const string &name,
+                               const string &defaultValue) const
 {
   const SGValue * val = getValue(name);
   if (val == 0)
-    return empty_string;
+    return defaultValue;
   else
     return val->getStringValue();
 }
@@ -1041,7 +1039,7 @@ get_base (const string &parent, const string &child,
  */
 SGPropertyNode::SGPropertyNode (const string &path = "",
                                SGPropertyList * props = 0)
-  : _props(props)
+  : _props(props), _node(0)
 {
   setPath(path);
 }
@@ -1052,6 +1050,8 @@ SGPropertyNode::SGPropertyNode (const string &path = "",
  */
 SGPropertyNode::~SGPropertyNode ()
 {
+  delete _node;
+  _node = 0;
 }
 
 
@@ -1089,24 +1089,6 @@ SGPropertyNode::getName () const
 }
 
 
-/**
- * Return the value of the current node.
- *
- * Currently, this does a lookup each time, but we could cache the
- * value safely as long as it's non-zero.
- *
- * Note that this will not create the value if it doesn't already exist.
- */
-SGValue *
-SGPropertyNode::getValue ()
-{
-  if (_props == 0 || _path.size() == 0)
-    return 0;
-  else
-    return _props->getValue(_path);
-}
-
-
 /**
  * Return the number of children for the current node.
  */
@@ -1143,17 +1125,18 @@ SGPropertyNode::size () const
  * A return value of true means success; otherwise, the node supplied
  * is unmodified.
  */
-bool
-SGPropertyNode::getParent (SGPropertyNode &parent) const
+SGPropertyNode &
+SGPropertyNode::getParent () const
 {
+  if (_node == 0)
+    _node = new SGPropertyNode();
+
   string::size_type pos = _path.rfind('/');
   if (pos != string::npos) {
-    parent.setPath(_path.substr(0, pos-1));
-    parent.setPropertyList(_props);
-    return true;
-  } else {
-    return false;
+    _node->setPropertyList(_props);
+    _node->setPath(_path.substr(0, pos-1));
   }
+  return *_node;
 }
 
 
@@ -1163,11 +1146,14 @@ SGPropertyNode::getParent (SGPropertyNode &parent) const
  * A return value of true means success; otherwise, the node supplied
  * is unmodified.
  */
-bool
-SGPropertyNode::getChild (SGPropertyNode &child, int n) const
+SGPropertyNode &
+SGPropertyNode::getChild (int n) const
 {
+  if (_node == 0)
+    _node = new SGPropertyNode();
+
   if (_props == 0)
-    return false;
+    return *_node;
 
   int s = 0;
   string base;
@@ -1180,12 +1166,9 @@ SGPropertyNode::getChild (SGPropertyNode &child, int n) const
   while (it != end) {
     if (get_base(pattern, it->first, base) && base != lastBase) {
       if (s == n) {
-       string path = _path;
-       path += '/';
-       path += base;
-       child.setPath(path);
-       child.setPropertyList(_props);
-       return true;
+       _node->setPropertyList(_props);
+       _node->setPath(_path + string("/") + base);
+       return *_node;
       } else {
        s++;
        lastBase = base;
@@ -1194,7 +1177,133 @@ SGPropertyNode::getChild (SGPropertyNode &child, int n) const
     it++;
   }
 
-  return false;
+  return *_node;
+}
+
+
+/**
+ * Return a node for an arbitrary subpath.
+ *
+ * Never returns 0.
+ */
+SGPropertyNode &
+SGPropertyNode::getSubNode (const string &subpath) const
+{
+  if (_node == 0)
+    _node = new SGPropertyNode();
+
+  _node->setPropertyList(_props);
+  _node->setPath(_path + string("/") + subpath);
+  return *_node;
 }
 
+
+/**
+ * Return the value of the current node.
+ *
+ * Currently, this does a lookup each time, but we could cache the
+ * value safely as long as it's non-zero.
+ *
+ * Note that this will not create the value if it doesn't already exist.
+ */
+SGValue *
+SGPropertyNode::getValue (const string &subpath)
+{
+  if (_props == 0 || _path.size() == 0)
+    return 0;
+
+  if (subpath.size() == 0)
+    return _props->getValue(_path);
+  else
+    return _props->getValue(_path + string("/") + subpath);
+}
+
+
+/**
+ * Return a bool value.
+ */
+bool
+SGPropertyNode::getBoolValue (const string &subpath, bool defaultValue) const
+{
+  if (_props == 0 || _path.size() == 0)
+    return defaultValue;
+
+  if (subpath == "")
+    return _props->getBoolValue(_path, defaultValue);
+  else
+    return _props->getBoolValue(_path + string("/") + subpath,
+                               defaultValue);
+}
+
+
+/**
+ * Return an int value.
+ */
+int
+SGPropertyNode::getIntValue (const string &subpath, int defaultValue) const
+{
+  if (_props == 0 || _path.size() == 0)
+    return defaultValue;
+
+  if (subpath == "")
+    return _props->getIntValue(_path, defaultValue);
+  else
+    return _props->getIntValue(_path + string("/") + subpath,
+                              defaultValue);
+}
+
+
+/**
+ * Return a float value.
+ */
+float
+SGPropertyNode::getFloatValue (const string &subpath, float defaultValue) const
+{
+  if (_props == 0 || _path.size() == 0)
+    return defaultValue;
+
+  if (subpath == "")
+    return _props->getFloatValue(_path, defaultValue);
+  else
+    return _props->getFloatValue(_path + string("/") + subpath,
+                                defaultValue);
+}
+
+
+/**
+ * Return a double value.
+ */
+double
+SGPropertyNode::getDoubleValue (const string &subpath,
+                               double defaultValue) const
+{
+  if (_props == 0 || _path.size() == 0)
+    return defaultValue;
+
+  if (subpath == "")
+    return _props->getDoubleValue(_path, defaultValue);
+  else
+    return _props->getDoubleValue(_path + string("/") + subpath,
+                                 defaultValue);
+}
+
+
+/**
+ * Return a string value.
+ */
+const string &
+SGPropertyNode::getStringValue (const string &subpath,
+                               const string &defaultValue) const
+{
+  if (_props == 0 || _path.size() == 0)
+    return defaultValue;
+
+  if (subpath == "")
+    return _props->getStringValue(_path, defaultValue);
+  else
+    return _props->getStringValue(_path + string("/") + subpath,
+                                 defaultValue);
+}
+
+
 // end of props.cxx
index d2d769025b767a58ff5c52af5cfa62e2f7123320..ebaaf7f29e40f4b3973e8d24da5ad08a34ae5993 100644 (file)
 
 #include <string>
 #include <map>
+#include <iostream>
 
 using std::string;
 using std::map;
+using std::istream;
+using std::ostream;
+
+#ifdef UNKNOWN
+#pragma warn A sloppy coder has defined UNKNOWN as a macro!
+#undef UNKNOWN
+#endif
+
+#ifdef BOOL
+#pragma warn A sloppy coder has defined BOOL as a macro!
+#undef BOOL
+#endif
+
+#ifdef INT
+#pragma warn A sloppy coder has defined INT as a macro!
+#undef INT
+#endif
+
+#ifdef FLOAT
+#pragma warn A sloppy coder has defined FLOAT as a macro!
+#undef FLOAT
+#endif
+
+#ifdef DOUBLE
+#pragma warn A sloppy coder has defined DOUBLE as a macro!
+#undef DOUBLE
+#endif
+
+#ifdef STRING
+#pragma warn A sloppy coder has defined STRING as a macro!
+#undef STRING
+#endif
 
 
 \f
@@ -80,7 +113,7 @@ public:
   virtual int getIntValue () const;
   virtual float getFloatValue () const;
   virtual double getDoubleValue () const;
-  virtual const string &getStringValue () const;
+  virtual const string & getStringValue () const;
 
                                // Setters.
   virtual bool setBoolValue (bool value);
@@ -231,11 +264,17 @@ public:
   virtual SGValue * getValue (const string &name, bool create = false);
   virtual const SGValue * getValue (const string &name) const;
 
-  virtual bool getBoolValue (const string &name) const;
-  virtual int getIntValue (const string &name) const;
-  virtual float getFloatValue (const string &name) const;
-  virtual double getDoubleValue (const string &name) const;
-  virtual const string &getStringValue (const string &name) const;
+  virtual bool getBoolValue (const string &name,
+                            bool defaultValue = false) const;
+  virtual int getIntValue (const string &name,
+                          int defaultValue = 0) const;
+  virtual float getFloatValue (const string &name,
+                              float defaultValue = 0.0) const;
+  virtual double getDoubleValue (const string &name,
+                                double defaultValue = 0.0L) const;
+  virtual const string & getStringValue (const string &name,
+                                        const string &defaultValue = "")
+    const;
 
   virtual bool setBoolValue (const string &name, bool value);
   virtual bool setIntValue (const string &name, int value);
@@ -331,17 +370,45 @@ public:
                                // Accessors for derived information.
   virtual int size () const;
   virtual const string &getName () const;
-  virtual SGValue * getValue ();
-  virtual bool getParent (SGPropertyNode &parent) const;
-  virtual bool getChild (SGPropertyNode &child, int n) const;
+  virtual SGPropertyNode &getParent () const;
+  virtual SGPropertyNode &getChild (int n) const;
+  virtual SGPropertyNode &getSubNode (const string &subpath) const;
+
+                               // Get values directly.
+  virtual SGValue * getValue (const string &subpath = "");
+  virtual bool getBoolValue (const string &subpath = "",
+                            bool defaultValue = false) const;
+  virtual int getIntValue (const string &subpath = "",
+                          int defaultValue = 0) const;
+  virtual float getFloatValue (const string &subpath = "",
+                              float defaultValue = 0.0) const;
+  virtual double getDoubleValue (const string &subpath = "",
+                                double defaultValue = 0.0L) const;
+  virtual const string &
+  getStringValue (const string &subpath = "",
+                 const string &defaultValue = "") const;
 
 private:
   string _path;
-  mutable string _name;                // for pointer persistence only
   SGPropertyList * _props;
+                               // for pointer persistence...
+                               // NOT THREAD SAFE!!!
+                               // (each thread must have its own node
+                               // object)
+  mutable string _name;
+  mutable SGPropertyNode * _node;
 };
 
 
+\f
+////////////////////////////////////////////////////////////////////////
+// Input and output.
+////////////////////////////////////////////////////////////////////////
+
+extern bool readPropertyList (istream &input, SGPropertyList * props);
+extern bool writePropertyList (ostream &output, const SGPropertyList * props);
+
+
 \f
 ////////////////////////////////////////////////////////////////////////
 // Global property manager.
diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx
new file mode 100644 (file)
index 0000000..8515fc7
--- /dev/null
@@ -0,0 +1,332 @@
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/xml/easyxml.hxx>
+
+#include "props.hxx"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+using std::istream;
+using std::ostream;
+using std::string;
+using std::vector;
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Visitor class for building the property list.
+////////////////////////////////////////////////////////////////////////
+
+class PropVisitor : public XMLVisitor
+{
+public:
+  PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {}
+  void startDocument ();
+  void startElement (const char * name, const XMLAttributes &atts);
+  void endElement (const char * name);
+  void data (const char * s, int length);
+  void warning (const char * message, int line, int col);
+  void error (const char * message, int line, int col);
+
+  bool isOK () const { return _ok; }
+
+private:
+
+  void pushState (const char * name) {
+    _states.push_back(_state);
+    _level++;
+    _state.name = name;
+    _state.type = SGValue::UNKNOWN;
+    _state.data = "";
+    _state.hasChildren = false;
+    _state.hasData = false;
+  }
+
+  void popState () {
+    _state = _states.back();
+    _states.pop_back();
+    _level--;
+  }
+
+  struct State
+  {
+    State () : hasChildren(false), hasData(false) {}
+    string name;
+    SGValue::Type type;
+    string data;
+    bool hasChildren;
+    bool hasData;
+  };
+
+  SGPropertyList * _props;
+  State _state;
+  vector<State> _states;
+  int _level;
+  bool _ok;
+};
+
+void
+PropVisitor::startDocument ()
+{
+  _level = 0;
+  _ok = true;
+}
+
+
+void
+PropVisitor::startElement (const char * name, const XMLAttributes &atts)
+{
+  if (!_ok)
+    return;
+
+  if (_level == 0 && strcmp(name, "PropertyList")) {
+    _ok = false;
+    FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \""
+          << name << "\" instead of \"PropertyList\"");
+    return;
+  }
+
+                               // Mixed content?
+  _state.hasChildren = true;
+  if (_state.hasData) {
+    FG_LOG(FG_INPUT, FG_ALERT,
+          "XML element has mixed elements and data in element "
+          << _state.name);
+    _ok = false;
+    return;
+  }
+
+                               // Start a new state.
+  pushState(name);
+
+                               // See if there's a type specified.
+  const char * type = atts.getValue("type");
+  if (type == 0 || !strcmp("unknown", type))
+    _state.type = SGValue::UNKNOWN;
+  else if (!strcmp("bool", type))
+    _state.type = SGValue::BOOL;
+  else if (!strcmp("int", type))
+    _state.type = SGValue::INT;
+  else if (!strcmp("float", type))
+    _state.type = SGValue::FLOAT;
+  else if (!strcmp("double", type))
+    _state.type = SGValue::DOUBLE;
+  else if (!strcmp("string", type))
+    _state.type = SGValue::STRING;
+  else
+    FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type
+          << ", using UNKNOWN");
+}
+
+void
+PropVisitor::endElement (const char * name)
+{
+  if (!_ok)
+    return;
+
+                               // See if there's a property to add.
+  if (_state.hasData) {
+    bool status = false;
+
+                               // Figure out the path name.
+    string path = "";
+    for (int i = 2; i < _level; i++) {
+      path += '/';
+      path += _states[i].name;
+    }
+    path += '/';
+    path += _state.name;
+
+                               // Set the value
+    switch (_state.type) {
+    case SGValue::BOOL:
+      if (_state.data == "true" || _state.data == "TRUE") {
+       status = _props->setBoolValue(path, true);
+      } else if (atof(_state.data.c_str()) != 0.0) {
+       status = _props->setBoolValue(path, true);
+      } else {
+       status =_props->setBoolValue(path, false);
+      }
+      break;
+    case SGValue::INT :
+      status = _props->setIntValue(path, atoi(_state.data.c_str()));
+      break;
+    case SGValue::FLOAT:
+      status = _props->setFloatValue(path, atof(_state.data.c_str()));
+      break;
+    case SGValue::DOUBLE:
+      status = _props->setDoubleValue(path, atof(_state.data.c_str()));
+      break;
+    case SGValue::STRING:
+      status = _props->setStringValue(path, _state.data);
+      break;
+    default:
+      status = _props->setUnknownValue(path, _state.data);
+      break;
+    }
+    if (!status)
+      FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property "
+            << path << " to " << _state.data);
+  }
+
+                               // Pop the stack.
+  popState();
+}
+
+void
+PropVisitor::data (const char * s, int length)
+{
+  if (!_ok)
+    return;
+
+                               // Check if there is any non-whitespace
+  if (!_state.hasData)
+    for (int i = 0; i < length; i++) 
+      if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r')
+       _state.hasData = true;
+
+  _state.data += string(s, length); // FIXME: inefficient
+}
+
+void
+PropVisitor::warning (const char * message, int line, int col)
+{
+  FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: "
+        << message << " (" << line << ',' << col << ')');
+}
+
+void
+PropVisitor::error (const char * message, int line, int col)
+{
+  FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: "
+        << message << " (" << line << ',' << col << ')');
+  _ok = false;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list reader.
+////////////////////////////////////////////////////////////////////////
+
+bool
+readPropertyList (istream &input, SGPropertyList * props)
+{
+  PropVisitor visitor(props);
+  return readXML(input, visitor) && visitor.isOK();
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list writer.
+////////////////////////////////////////////////////////////////////////
+
+#define INDENT_STEP 2
+
+/**
+ * Return the type name.
+ */
+static const char *
+getTypeName (SGValue::Type type)
+{
+  switch (type) {
+  case SGValue::UNKNOWN:
+    return "unknown";
+  case SGValue::BOOL:
+    return "bool";
+  case SGValue::INT:
+    return "int";
+  case SGValue::FLOAT:
+    return "float";
+  case SGValue::DOUBLE:
+    return "double";
+  case SGValue::STRING:
+    return "string";
+  }
+}
+
+
+/**
+ * Escape characters for output.
+ */
+static void
+writeData (ostream &output, const string &data)
+{
+  for (int i = 0; i < data.size(); i++) {
+    switch (data[i]) {
+    case '&':
+      output << "&amp;";
+      break;
+    case '<':
+      output << "&lt;";
+      break;
+    case '>':
+      output << "&gt;";
+      break;
+    default:
+      output << data[i];
+      break;
+    }
+  }
+}
+
+static void
+doIndent (ostream &output, int indent)
+{
+  while (indent-- > 0) {
+    output << ' ';
+  }
+}
+
+
+static bool
+writeNode (ostream &output, SGPropertyNode node, int indent)
+{
+  const string &name = node.getName();
+  int size = node.size();
+
+                               // Write out the literal value, if any.
+  SGValue * value = node.getValue();
+  if (value != 0) {
+    SGValue::Type type = value->getType();
+    doIndent(output, indent);
+    output << '<' << name;
+    if (type != SGValue::UNKNOWN)
+      output << " type=\"" << getTypeName(type) << '"';
+    output << '>';
+    writeData(output, value->getStringValue());
+    output << "</" << name << '>' << endl;
+  }
+
+                               // Write out the children, if any.
+  if (size > 0) {
+    doIndent(output, indent);
+    output << '<' << name << '>' << endl;;
+    for (int i = 0; i < size; i++) {
+      writeNode(output, node.getChild(i), indent + INDENT_STEP);
+    }
+    doIndent(output, indent);
+    output << "</" << name << '>' << endl;
+  }
+}
+
+bool
+writePropertyList (ostream &output, const SGPropertyList * props)
+{
+  SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME
+
+  output << "<?xml version=\"1.0\"?>" << endl << endl;
+  output << "<PropertyList>" << endl;
+
+  for (int i = 0; i < root.size(); i++) {
+    writeNode(output, root.getChild(i), INDENT_STEP);
+  }
+
+  output << "</PropertyList>" << endl;
+}
index 2331fc375aea9d49ade461e689a2d8942b5614d5..81038fec1a931b043aa12951cca6d6607ddbd6d3 100644 (file)
@@ -20,9 +20,10 @@ int
 XMLAttributes::findAttribute (const char * name) const
 {
   int s = size();
-  for (int i = 0; i < s; i++)
+  for (int i = 0; i < s; i++) {
     if (strcmp(name, getName(i)) == 0)
       return i;
+  }
   return -1;
 }
 
@@ -39,7 +40,7 @@ XMLAttributes::getValue (const char * name) const
   if (pos >= 0)
     return getValue(pos);
   else
-    return getValue(0);
+    return 0;
 }
 
 \f
@@ -122,9 +123,9 @@ class ExpatAtts : public XMLAttributes
 public:
   ExpatAtts (const char ** atts) : _atts(atts) {}
   
-  int size () const;
-  const char * getName (int i) const;
-  const char * getValue (int i) const;
+  virtual int size () const;
+  virtual const char * getName (int i) const;
+  virtual const char * getValue (int i) const;
   
 private:
   const char ** _atts;
@@ -162,8 +163,7 @@ ExpatAtts::getValue (int i) const
 static void
 start_element (void * userData, const char * name, const char ** atts)
 {
-  ExpatAtts attributes(atts);
-  VISITOR.startElement(name, attributes);
+  VISITOR.startElement(name, ExpatAtts(atts));
 }
 
 static void