simgear/math/Makefile \
simgear/metar/Makefile \
simgear/misc/Makefile \
+ simgear/props/Makefile \
simgear/route/Makefile \
+ simgear/scene/Makefile \
+ simgear/scene/model/Makefile \
simgear/screen/Makefile \
simgear/serial/Makefile \
simgear/sky/Makefile \
magvar \
math \
$(METAR_DIRS) \
+ props \
route \
+ scene \
screen \
serial \
sky \
include_HEADERS = \
commands.hxx \
exception.hxx \
- props.hxx \
- props_io.hxx \
sg_path.hxx \
sgstream.hxx \
stopwatch.hxx \
libsgmisc_a_SOURCES = \
commands.cxx \
exception.cxx \
- props.cxx \
- props_io.cxx \
sg_path.cxx \
sgstream.cxx \
strutils.cxx \
texcoord.cxx \
zfstream.cxx
-noinst_PROGRAMS = props_test tabbed_value_test
-
-props_test_SOURCES = props_test.cxx
-props_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a
+noinst_PROGRAMS = tabbed_value_test
tabbed_value_test_SOURCES = tabbed_values_test.cxx
-tabbed_value_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a
+tabbed_value_test_LDADD = \
+ $(top_builddir)/simgear/misc/libsgmisc.a \
+ $(top_builddir)/simgear/xml/libsgxml.a \
+ $(top_builddir)/simgear/debug/libsgdebug.a
INCLUDES = -I$(top_srcdir)
//
// $Id$
+#include <simgear/props/props_io.hxx>
+
#include "commands.hxx"
-#include "props_io.hxx"
\f
#include <map>
#include <vector>
-#include "props.hxx"
+#include <simgear/props/props.hxx>
SG_USING_STD(string);
SG_USING_STD(map);
+++ /dev/null
-// props.cxx - implementation of a property list.
-// Started Fall 2000 by David Megginson, david@megginson.com
-// This code is released into the Public Domain.
-//
-// See props.html for documentation [replace with URL when available].
-//
-// $Id$
-
-#include "props.hxx"
-
-#include <algorithm>
-#include <stdio.h>
-#include <string.h>
-
-#if PROPS_STANDALONE
-
-#include <iostream>
-using std::cerr;
-using std::endl;
-using std::find;
-using std::sort;
-using std::vector;
-
-#else
-
-#include <simgear/compiler.h>
-#include <simgear/debug/logstream.hxx>
-
-SG_USING_STD(sort);
-SG_USING_STD(find);
-SG_USING_STD(vector);
-
-#ifdef _MSC_VER
-// MSVC is buggy, and needs something strange here
-SG_USING_STD(vector<SGPropertyNode_ptr>);
-SG_USING_STD(vector<SGPropertyChangeListener *>);
-SG_USING_STD(vector<SGPropertyNode *>);
-#endif
-
-#endif
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Local classes.
-////////////////////////////////////////////////////////////////////////
-
-/**
- * Comparator class for sorting by index.
- */
-class CompareIndices
-{
-public:
- int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
- return (n1->getIndex() < n2->getIndex());
- }
-};
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Convenience macros for value access.
-////////////////////////////////////////////////////////////////////////
-
-#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
-#define TEST_WRITE if (!getAttribute(WRITE)) return false
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Default values for every type.
-////////////////////////////////////////////////////////////////////////
-
-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 char * const SGRawValue<const char *>::DefaultValue = "";
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Local path normalization code.
-////////////////////////////////////////////////////////////////////////
-
-/**
- * A component in a path.
- */
-struct PathComponent
-{
- string name;
- int index;
-};
-
-/**
- * Parse the name for a path component.
- *
- * Name: [_a-zA-Z][-._a-zA-Z0-9]*
- */
-static inline const string
-parse_name (const string &path, int &i)
-{
- string name = "";
- int max = path.size();
-
- if (path[i] == '.') {
- i++;
- if (i < max && path[i] == '.') {
- i++;
- name = "..";
- } else {
- name = ".";
- }
- if (i < max && path[i] != '/')
- throw string(string("Illegal character after ") + name);
- }
-
- else if (isalpha(path[i]) || path[i] == '_') {
- name += path[i];
- i++;
-
- // The rules inside a name are a little
- // less restrictive.
- while (i < max) {
- if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
- path[i] == '-' || path[i] == '.') {
- name += path[i];
- } else if (path[i] == '[' || path[i] == '/') {
- break;
- } else {
- throw string("name may contain only ._- and alphanumeric characters");
- }
- i++;
- }
- }
-
- else {
- if (name.size() == 0)
- throw string("name must begin with alpha or '_'");
- }
-
- return name;
-}
-
-
-/**
- * Parse the optional integer index for a path component.
- *
- * Index: "[" [0-9]+ "]"
- */
-static inline int
-parse_index (const string &path, int &i)
-{
- int index = 0;
-
- if (path[i] != '[')
- return 0;
- else
- i++;
-
- for (int max = path.size(); i < max; i++) {
- if (isdigit(path[i])) {
- index = (index * 10) + (path[i] - '0');
- } else if (path[i] == ']') {
- i++;
- return index;
- } else {
- break;
- }
- }
-
- throw string("unterminated index (looking for ']')");
-}
-
-
-/**
- * Parse a single path component.
- *
- * Component: Name Index?
- */
-static inline PathComponent
-parse_component (const string &path, int &i)
-{
- PathComponent component;
- component.name = parse_name(path, i);
- if (component.name[0] != '.')
- component.index = parse_index(path, i);
- else
- component.index = -1;
- return component;
-}
-
-
-/**
- * Parse a path into its components.
- */
-static void
-parse_path (const string &path, vector<PathComponent> &components)
-{
- int pos = 0;
- int max = path.size();
-
- // Check for initial '/'
- if (path[pos] == '/') {
- PathComponent root;
- root.name = "";
- root.index = -1;
- components.push_back(root);
- pos++;
- while (pos < max && path[pos] == '/')
- pos++;
- }
-
- while (pos < max) {
- components.push_back(parse_component(path, pos));
- while (pos < max && path[pos] == '/')
- pos++;
- }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Other static utility functions.
-////////////////////////////////////////////////////////////////////////
-
-
-static char *
-copy_string (const char * s)
-{
- // FIXME: potential buffer overflow.
- // For some reason, strnlen and
- // strncpy cause all kinds of crashes.
- char * copy = new char[strlen(s) + 1];
- strcpy(copy, s);
- return copy;
-}
-
-static bool
-compare_strings (const char * s1, const char * s2)
-{
- return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
-}
-
-/**
- * Locate a child node by name and index.
- */
-static int
-find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
-{
- int nNodes = nodes.size();
- for (int i = 0; i < nNodes; i++) {
- SGPropertyNode * node = nodes[i];
- if (compare_strings(node->getName(), name) && node->getIndex() == index)
- return i;
- }
- return -1;
-}
-
-
-/**
- * Locate another node, given a relative path.
- */
-static SGPropertyNode *
-find_node (SGPropertyNode * current,
- const vector<PathComponent> &components,
- 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 >= (int)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)
- throw string("Attempt to move past root with '..'");
- else
- return find_node(parent, components, position + 1, create);
- }
-
- // Otherwise, a child name
- else {
- SGPropertyNode * child =
- current->getChild(components[position].name.c_str(),
- components[position].index,
- create);
- return find_node(child, components, position + 1, create);
- }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Private methods from SGPropertyNode (may be inlined for speed).
-////////////////////////////////////////////////////////////////////////
-
-inline bool
-SGPropertyNode::get_bool () const
-{
- if (_tied)
- return _value.bool_val->getValue();
- else
- return _local_val.bool_val;
-}
-
-inline int
-SGPropertyNode::get_int () const
-{
- if (_tied)
- return _value.int_val->getValue();
- else
- return _local_val.int_val;
-}
-
-inline long
-SGPropertyNode::get_long () const
-{
- if (_tied)
- return _value.long_val->getValue();
- else
- return _local_val.long_val;
-}
-
-inline float
-SGPropertyNode::get_float () const
-{
- if (_tied)
- return _value.float_val->getValue();
- else
- return _local_val.float_val;
-}
-
-inline double
-SGPropertyNode::get_double () const
-{
- if (_tied)
- return _value.double_val->getValue();
- else
- return _local_val.double_val;
-}
-
-inline const char *
-SGPropertyNode::get_string () const
-{
- if (_tied)
- return _value.string_val->getValue();
- else
- return _local_val.string_val;
-}
-
-inline bool
-SGPropertyNode::set_bool (bool val)
-{
- if (_tied) {
- if (_value.bool_val->setValue(val)) {
- fireValueChanged();
- return true;
- } else {
- return false;
- }
- } else {
- _local_val.bool_val = val;
- fireValueChanged();
- return true;
- }
-}
-
-inline bool
-SGPropertyNode::set_int (int val)
-{
- if (_tied) {
- if (_value.int_val->setValue(val)) {
- fireValueChanged();
- return true;
- } else {
- return false;
- }
- } else {
- _local_val.int_val = val;
- fireValueChanged();
- return true;
- }
-}
-
-inline bool
-SGPropertyNode::set_long (long val)
-{
- if (_tied) {
- if (_value.long_val->setValue(val)) {
- fireValueChanged();
- return true;
- } else {
- return false;
- }
- } else {
- _local_val.long_val = val;
- fireValueChanged();
- return true;
- }
-}
-
-inline bool
-SGPropertyNode::set_float (float val)
-{
- if (_tied) {
- if (_value.float_val->setValue(val)) {
- fireValueChanged();
- return true;
- } else {
- return false;
- }
- } else {
- _local_val.float_val = val;
- fireValueChanged();
- return true;
- }
-}
-
-inline bool
-SGPropertyNode::set_double (double val)
-{
- if (_tied) {
- if (_value.double_val->setValue(val)) {
- fireValueChanged();
- return true;
- } else {
- return false;
- }
- } else {
- _local_val.double_val = val;
- fireValueChanged();
- return true;
- }
-}
-
-inline bool
-SGPropertyNode::set_string (const char * val)
-{
- if (_tied) {
- if (_value.string_val->setValue(val)) {
- fireValueChanged();
- return true;
- } else {
- return false;
- }
- } else {
- delete [] _local_val.string_val;
- _local_val.string_val = copy_string(val);
- fireValueChanged();
- return true;
- }
-}
-
-void
-SGPropertyNode::clear_value ()
-{
- switch (_type) {
- case NONE:
- break;
- case ALIAS:
- _value.alias = 0;
- break;
- case BOOL:
- if (_tied) {
- delete _value.bool_val;
- _value.bool_val = 0;
- }
- _local_val.bool_val = SGRawValue<bool>::DefaultValue;
- break;
- case INT:
- if (_tied) {
- delete _value.int_val;
- _value.int_val = 0;
- }
- _local_val.int_val = SGRawValue<int>::DefaultValue;
- break;
- case LONG:
- if (_tied) {
- delete _value.long_val;
- _value.long_val = 0L;
- }
- _local_val.long_val = SGRawValue<long>::DefaultValue;
- break;
- case FLOAT:
- if (_tied) {
- delete _value.float_val;
- _value.float_val = 0;
- }
- _local_val.float_val = SGRawValue<float>::DefaultValue;
- break;
- case DOUBLE:
- if (_tied) {
- delete _value.double_val;
- _value.double_val = 0;
- }
- _local_val.double_val = SGRawValue<double>::DefaultValue;
- break;
- case STRING:
- case UNSPECIFIED:
- if (_tied) {
- delete _value.string_val;
- _value.string_val = 0;
- } else {
- delete [] _local_val.string_val;
- }
- _local_val.string_val = 0;
- break;
- }
- _tied = false;
- _type = NONE;
-}
-
-
-/**
- * Get the value as a string.
- */
-const char *
-SGPropertyNode::make_string () const
-{
- if (!getAttribute(READ))
- return "";
-
- switch (_type) {
- case ALIAS:
- return _value.alias->getStringValue();
- case BOOL:
- if (get_bool())
- return "true";
- else
- return "false";
- case INT:
- sprintf(_buffer, "%d", get_int());
- return _buffer;
- case LONG:
- sprintf(_buffer, "%ld", get_long());
- return _buffer;
- case FLOAT:
- sprintf(_buffer, "%f", get_float());
- return _buffer;
- case DOUBLE:
- sprintf(_buffer, "%f", get_double());
- return _buffer;
- case STRING:
- case UNSPECIFIED:
- return get_string();
- case NONE:
- default:
- return "";
- }
-}
-
-/**
- * Trace a write access for a property.
- */
-void
-SGPropertyNode::trace_write () const
-{
-#if PROPS_STANDALONE
- cerr << "TRACE: Write node " << getPath () << ", value\""
- << make_string() << '"' << endl;
-#else
- SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
- << ", value\"" << make_string() << '"');
-#endif
-}
-
-/**
- * Trace a read access for a property.
- */
-void
-SGPropertyNode::trace_read () const
-{
-#if PROPS_STANDALONE
- cerr << "TRACE: Write node " << getPath () << ", value \""
- << make_string() << '"' << endl;
-#else
- SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath()
- << ", value \"" << make_string() << '"');
-#endif
-}
-
-/**
- * Increment reference counter
- */
-void
-SGPropertyNode::incrementRef()
-{
- ++_count;
-}
-
-/**
- * Decrement reference counter
- */
-int
-SGPropertyNode::decrementRef()
-{
- return --_count;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Public methods from SGPropertyNode.
-////////////////////////////////////////////////////////////////////////
-
-/**
- * Last used attribute
- * Update as needed when enum Attribute is changed
- */
-const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE;
-
-/**
- * Default constructor: always creates a root node.
- */
-SGPropertyNode::SGPropertyNode ()
- : _name(copy_string("")),
- _display_name(0),
- _index(0),
- _parent(0),
- _path(0),
- _path_cache(0),
- _type(NONE),
- _tied(false),
- _attr(READ|WRITE),
- _count(0),
- _listeners(0)
-{
- _local_val.string_val = 0;
-}
-
-
-/**
- * Copy constructor.
- */
-SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
- : _display_name(0),
- _index(node._index),
- _parent(0), // don't copy the parent
- _path(0),
- _path_cache(0),
- _type(node._type),
- _tied(node._tied),
- _attr(node._attr),
- _count(0),
- _listeners(0) // CHECK!!
-{
- _name = copy_string(node._name);
- _local_val.string_val = 0;
- switch (_type) {
- case NONE:
- break;
- case ALIAS:
- _value.alias = node._value.alias;
- _tied = false;
- break;
- case BOOL:
- if (_tied) {
- _tied = true;
- _value.bool_val = node._value.bool_val->clone();
- } else {
- _tied = false;
- set_bool(node.get_bool());
- }
- break;
- case INT:
- if (_tied) {
- _tied = true;
- _value.int_val = node._value.int_val->clone();
- } else {
- _tied = false;
- set_int(node.get_int());
- }
- break;
- case LONG:
- if (_tied) {
- _tied = true;
- _value.long_val = node._value.long_val->clone();
- } else {
- _tied = false;
- set_long(node.get_long());
- }
- break;
- case FLOAT:
- if (_tied) {
- _tied = true;
- _value.float_val = node._value.float_val->clone();
- } else {
- _tied = false;
- set_float(node.get_float());
- }
- break;
- case DOUBLE:
- if (_tied) {
- _tied = true;
- _value.double_val = node._value.double_val->clone();
- } else {
- _tied = false;
- set_double(node.get_double());
- }
- break;
- case STRING:
- case UNSPECIFIED:
- if (_tied) {
- _tied = true;
- _value.string_val = node._value.string_val->clone();
- } else {
- _tied = false;
- set_string(node.get_string());
- }
- break;
- }
-}
-
-
-/**
- * Convenience constructor.
- */
-SGPropertyNode::SGPropertyNode (const char * name,
- int index,
- SGPropertyNode * parent)
- : _display_name(0),
- _index(index),
- _parent(parent),
- _path(0),
- _path_cache(0),
- _type(NONE),
- _tied(false),
- _attr(READ|WRITE),
- _count(0),
- _listeners(0)
-{
- _name = copy_string(name);
- _local_val.string_val = 0;
-}
-
-
-/**
- * Destructor.
- */
-SGPropertyNode::~SGPropertyNode ()
-{
- delete [] _name;
- delete [] _display_name;
- delete [] _path;
- delete _path_cache;
- clear_value();
- delete _listeners;
-}
-
-
-/**
- * Alias to another node.
- */
-bool
-SGPropertyNode::alias (SGPropertyNode * target)
-{
- if (target == 0 || _type == ALIAS || _tied)
- return false;
- clear_value();
- _value.alias = target;
- _type = ALIAS;
- return true;
-}
-
-
-/**
- * Alias to another node by path.
- */
-bool
-SGPropertyNode::alias (const char * path)
-{
- return alias(getNode(path, true));
-}
-
-
-/**
- * Remove an alias.
- */
-bool
-SGPropertyNode::unalias ()
-{
- if (_type != ALIAS)
- return false;
- _type = NONE;
- _value.alias = 0;
- return true;
-}
-
-
-/**
- * Get the target of an alias.
- */
-SGPropertyNode *
-SGPropertyNode::getAliasTarget ()
-{
- return (_type == ALIAS ? _value.alias : 0);
-}
-
-
-const SGPropertyNode *
-SGPropertyNode::getAliasTarget () const
-{
- return (_type == ALIAS ? _value.alias : 0);
-}
-
-
-/**
- * Get a non-const child by index.
- */
-SGPropertyNode *
-SGPropertyNode::getChild (int position)
-{
- if (position >= 0 && position < nChildren())
- return _children[position];
- else
- return 0;
-}
-
-
-/**
- * Get a const child by index.
- */
-const SGPropertyNode *
-SGPropertyNode::getChild (int position) const
-{
- if (position >= 0 && position < nChildren())
- return _children[position];
- else
- return 0;
-}
-
-
-/**
- * Get a non-const child by name and index, creating if necessary.
- */
-SGPropertyNode *
-SGPropertyNode::getChild (const char * name, int index, bool create)
-{
- int pos = find_child(name, index, _children);
- if (pos >= 0) {
- return _children[pos];
- } else if (create) {
- SGPropertyNode_ptr node;
- pos = find_child(name, index, _removedChildren);
- if (pos >= 0) {
- vector<SGPropertyNode_ptr>::iterator it = _removedChildren.begin();
- it += pos;
- node = _removedChildren[pos];
- _removedChildren.erase(it);
- node->setAttribute(REMOVED, false);
- } else {
- node = new SGPropertyNode(name, index, this);
- }
- _children.push_back(node);
- fireChildAdded(node);
- return node;
- } else {
- return 0;
- }
-}
-
-
-/**
- * Get a const child by name and index.
- */
-const SGPropertyNode *
-SGPropertyNode::getChild (const char * name, int index) const
-{
- int pos = find_child(name, index, _children);
- if (pos >= 0)
- return _children[pos];
- else
- return 0;
-}
-
-
-/**
- * Get all children with the same name (but different indices).
- */
-vector<SGPropertyNode_ptr>
-SGPropertyNode::getChildren (const char * name) const
-{
- vector<SGPropertyNode_ptr> children;
- int max = _children.size();
-
- for (int i = 0; i < max; i++)
- if (compare_strings(_children[i]->getName(), name))
- children.push_back(_children[i]);
-
- sort(children.begin(), children.end(), CompareIndices());
- return children;
-}
-
-
-/**
- * Remove a child node
- */
-SGPropertyNode_ptr
-SGPropertyNode::removeChild (const char * name, int index, bool keep)
-{
- SGPropertyNode_ptr ret;
- int pos = find_child(name, index, _children);
- if (pos >= 0) {
- vector<SGPropertyNode_ptr>::iterator it = _children.begin();
- it += pos;
- SGPropertyNode_ptr node = _children[pos];
- _children.erase(it);
- if (keep) {
- _removedChildren.push_back(node);
- }
- node->setAttribute(REMOVED, true);
- ret = node;
- fireChildRemoved(node);
- }
- return ret;
-}
-
-
-const char *
-SGPropertyNode::getDisplayName (bool simplify) const
-{
- string display = _name;
- if (_index != 0 || !simplify) {
- char buffer[64];
- sprintf(buffer, "[%d]", _index);
- display += buffer;
- }
- _display_name = copy_string(display.c_str());
- return _display_name;
-}
-
-
-const char *
-SGPropertyNode::getPath (bool simplify) const
-{
- // Calculate the complete path only once.
- if (_path == 0) {
- string path;
- if (_parent == 0) {
- path = "";
- } else {
- path = _parent->getPath(simplify);
- path += '/';
- path += getDisplayName(simplify);
- }
- _path = copy_string(path.c_str());
- }
-
- return _path;
-}
-
-SGPropertyNode::Type
-SGPropertyNode::getType () const
-{
- if (_type == ALIAS)
- return _value.alias->getType();
- else
- return _type;
-}
-
-
-bool
-SGPropertyNode::getBoolValue () const
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == BOOL)
- return get_bool();
-
- if (getAttribute(TRACE_READ))
- trace_read();
- if (!getAttribute(READ))
- return SGRawValue<bool>::DefaultValue;
- 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:
- return get_double() == 0.0L ? false : true;
- case STRING:
- case UNSPECIFIED:
- return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
- case NONE:
- default:
- return SGRawValue<bool>::DefaultValue;
- }
-}
-
-int
-SGPropertyNode::getIntValue () const
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == INT)
- return get_int();
-
- if (getAttribute(TRACE_READ))
- trace_read();
- if (!getAttribute(READ))
- return SGRawValue<int>::DefaultValue;
- switch (_type) {
- case ALIAS:
- return _value.alias->getIntValue();
- case BOOL:
- return int(get_bool());
- case INT:
- return get_int();
- case LONG:
- return int(get_long());
- case FLOAT:
- return int(get_float());
- case DOUBLE:
- return int(get_double());
- case STRING:
- case UNSPECIFIED:
- return atoi(get_string());
- case NONE:
- default:
- return SGRawValue<int>::DefaultValue;
- }
-}
-
-long
-SGPropertyNode::getLongValue () const
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == LONG)
- return get_long();
-
- if (getAttribute(TRACE_READ))
- trace_read();
- if (!getAttribute(READ))
- return SGRawValue<long>::DefaultValue;
- 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 UNSPECIFIED:
- return strtol(get_string(), 0, 0);
- case NONE:
- default:
- return SGRawValue<long>::DefaultValue;
- }
-}
-
-float
-SGPropertyNode::getFloatValue () const
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == FLOAT)
- return get_float();
-
- if (getAttribute(TRACE_READ))
- trace_read();
- if (!getAttribute(READ))
- return SGRawValue<float>::DefaultValue;
- switch (_type) {
- case ALIAS:
- return _value.alias->getFloatValue();
- case BOOL:
- return float(get_bool());
- case INT:
- return float(get_int());
- case LONG:
- return float(get_long());
- case FLOAT:
- return get_float();
- case DOUBLE:
- return float(get_double());
- case STRING:
- case UNSPECIFIED:
- return atof(get_string());
- case NONE:
- default:
- return SGRawValue<float>::DefaultValue;
- }
-}
-
-double
-SGPropertyNode::getDoubleValue () const
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == DOUBLE)
- return get_double();
-
- if (getAttribute(TRACE_READ))
- trace_read();
- if (!getAttribute(READ))
- return SGRawValue<double>::DefaultValue;
-
- switch (_type) {
- case ALIAS:
- return _value.alias->getDoubleValue();
- case BOOL:
- return double(get_bool());
- case INT:
- return double(get_int());
- case LONG:
- return double(get_long());
- case FLOAT:
- return double(get_float());
- case DOUBLE:
- return get_double();
- case STRING:
- case UNSPECIFIED:
- return strtod(get_string(), 0);
- case NONE:
- default:
- return SGRawValue<double>::DefaultValue;
- }
-}
-
-const char *
-SGPropertyNode::getStringValue () const
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == STRING)
- return get_string();
-
- if (getAttribute(TRACE_READ))
- trace_read();
- if (!getAttribute(READ))
- return SGRawValue<const char *>::DefaultValue;
- return make_string();
-}
-
-bool
-SGPropertyNode::setBoolValue (bool value)
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == BOOL)
- return set_bool(value);
-
- bool result = false;
- TEST_WRITE;
- if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
- _tied = false;
- _type = BOOL;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setBoolValue(value);
- break;
- case BOOL:
- result = set_bool(value);
- break;
- case INT:
- result = set_int(int(value));
- break;
- case LONG:
- result = set_long(long(value));
- break;
- case FLOAT:
- result = set_float(float(value));
- break;
- case DOUBLE:
- result = set_double(double(value));
- break;
- case STRING:
- case UNSPECIFIED:
- result = set_string(value ? "true" : "false");
- break;
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::setIntValue (int value)
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == INT)
- return set_int(value);
-
- bool result = false;
- TEST_WRITE;
- if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
- _type = INT;
- _local_val.int_val = 0;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setIntValue(value);
- break;
- case BOOL:
- result = set_bool(value == 0 ? false : true);
- break;
- case INT:
- result = set_int(value);
- break;
- case LONG:
- result = set_long(long(value));
- break;
- case FLOAT:
- result = set_float(float(value));
- break;
- case DOUBLE:
- result = set_double(double(value));
- break;
- case STRING:
- case UNSPECIFIED: {
- char buf[128];
- sprintf(buf, "%d", value);
- result = set_string(buf);
- break;
- }
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::setLongValue (long value)
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == LONG)
- return set_long(value);
-
- bool result = false;
- TEST_WRITE;
- if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
- _type = LONG;
- _local_val.long_val = 0L;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setLongValue(value);
- break;
- case BOOL:
- result = set_bool(value == 0L ? false : true);
- break;
- case INT:
- result = set_int(int(value));
- break;
- case LONG:
- result = set_long(value);
- break;
- case FLOAT:
- result = set_float(float(value));
- break;
- case DOUBLE:
- result = set_double(double(value));
- break;
- case STRING:
- case UNSPECIFIED: {
- char buf[128];
- sprintf(buf, "%ld", value);
- result = set_string(buf);
- break;
- }
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::setFloatValue (float value)
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == FLOAT)
- return set_float(value);
-
- bool result = false;
- TEST_WRITE;
- if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
- _type = FLOAT;
- _local_val.float_val = 0;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setFloatValue(value);
- break;
- case BOOL:
- result = set_bool(value == 0.0 ? false : true);
- break;
- case INT:
- result = set_int(int(value));
- break;
- case LONG:
- result = set_long(long(value));
- break;
- case FLOAT:
- result = set_float(value);
- break;
- case DOUBLE:
- result = set_double(double(value));
- break;
- case STRING:
- case UNSPECIFIED: {
- char buf[128];
- sprintf(buf, "%f", value);
- result = set_string(buf);
- break;
- }
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::setDoubleValue (double value)
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == DOUBLE)
- return set_double(value);
-
- bool result = false;
- TEST_WRITE;
- if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
- _local_val.double_val = value;
- _type = DOUBLE;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setDoubleValue(value);
- break;
- case BOOL:
- result = set_bool(value == 0.0L ? false : true);
- break;
- case INT:
- result = set_int(int(value));
- break;
- case LONG:
- result = set_long(long(value));
- break;
- case FLOAT:
- result = set_float(float(value));
- break;
- case DOUBLE:
- result = set_double(value);
- break;
- case STRING:
- case UNSPECIFIED: {
- char buf[128];
- sprintf(buf, "%f", value);
- result = set_string(buf);
- break;
- }
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::setStringValue (const char * value)
-{
- // Shortcut for common case
- if (_attr == (READ|WRITE) && _type == STRING)
- return set_string(value);
-
- bool result = false;
- TEST_WRITE;
- if (_type == NONE || _type == UNSPECIFIED) {
- clear_value();
- _type = STRING;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setStringValue(value);
- break;
- case BOOL:
- result = set_bool((compare_strings(value, "true")
- || atoi(value)) ? true : false);
- break;
- case INT:
- result = set_int(atoi(value));
- break;
- case LONG:
- result = set_long(strtol(value, 0, 0));
- break;
- case FLOAT:
- result = set_float(atof(value));
- break;
- case DOUBLE:
- result = set_double(strtod(value, 0));
- break;
- case STRING:
- case UNSPECIFIED:
- result = set_string(value);
- break;
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::setUnspecifiedValue (const char * value)
-{
- bool result = false;
- TEST_WRITE;
- if (_type == NONE) {
- clear_value();
- _type = UNSPECIFIED;
- }
-
- switch (_type) {
- case ALIAS:
- result = _value.alias->setUnspecifiedValue(value);
- break;
- case BOOL:
- result = set_bool((compare_strings(value, "true")
- || atoi(value)) ? true : false);
- break;
- case INT:
- result = set_int(atoi(value));
- break;
- case LONG:
- result = set_long(strtol(value, 0, 0));
- break;
- case FLOAT:
- result = set_float(atof(value));
- break;
- case DOUBLE:
- result = set_double(strtod(value, 0));
- break;
- case STRING:
- case UNSPECIFIED:
- result = set_string(value);
- break;
- case NONE:
- default:
- break;
- }
-
- if (getAttribute(TRACE_WRITE))
- trace_write();
- return result;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<bool> &rawValue, bool useDefault)
-{
- if (_type == ALIAS || _tied)
- return false;
-
- useDefault = useDefault && hasValue();
- bool old_val = false;
- if (useDefault)
- old_val = getBoolValue();
-
- clear_value();
- _type = BOOL;
- _tied = true;
- _value.bool_val = rawValue.clone();
-
- if (useDefault)
- setBoolValue(old_val);
-
- return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<int> &rawValue, bool useDefault)
-{
- if (_type == ALIAS || _tied)
- return false;
-
- useDefault = useDefault && hasValue();
- int old_val = 0;
- if (useDefault)
- old_val = getIntValue();
-
- clear_value();
- _type = INT;
- _tied = true;
- _value.int_val = rawValue.clone();
-
- if (useDefault)
- setIntValue(old_val);
-
- return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<long> &rawValue, bool useDefault)
-{
- if (_type == ALIAS || _tied)
- return false;
-
- useDefault = useDefault && hasValue();
- long old_val = 0;
- if (useDefault)
- old_val = getLongValue();
-
- clear_value();
- _type = LONG;
- _tied = true;
- _value.long_val = rawValue.clone();
-
- if (useDefault)
- setLongValue(old_val);
-
- return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<float> &rawValue, bool useDefault)
-{
- if (_type == ALIAS || _tied)
- return false;
-
- useDefault = useDefault && hasValue();
- float old_val = 0.0;
- if (useDefault)
- old_val = getFloatValue();
-
- clear_value();
- _type = FLOAT;
- _tied = true;
- _value.float_val = rawValue.clone();
-
- if (useDefault)
- setFloatValue(old_val);
-
- return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<double> &rawValue, bool useDefault)
-{
- if (_type == ALIAS || _tied)
- return false;
-
- useDefault = useDefault && hasValue();
- double old_val = 0.0;
- if (useDefault)
- old_val = getDoubleValue();
-
- clear_value();
- _type = DOUBLE;
- _tied = true;
- _value.double_val = rawValue.clone();
-
- if (useDefault)
- setDoubleValue(old_val);
-
- return true;
-
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<const char *> &rawValue, bool useDefault)
-{
- if (_type == ALIAS || _tied)
- return false;
-
- useDefault = useDefault && hasValue();
- string old_val;
- if (useDefault)
- old_val = getStringValue();
-
- clear_value();
- _type = STRING;
- _tied = true;
- _value.string_val = rawValue.clone();
-
- if (useDefault)
- setStringValue(old_val.c_str());
-
- return true;
-}
-
-bool
-SGPropertyNode::untie ()
-{
- if (!_tied)
- return false;
-
- switch (_type) {
- case BOOL: {
- bool val = getBoolValue();
- clear_value();
- _type = BOOL;
- _local_val.bool_val = val;
- break;
- }
- case INT: {
- int val = getIntValue();
- clear_value();
- _type = INT;
- _local_val.int_val = val;
- break;
- }
- case LONG: {
- long val = getLongValue();
- clear_value();
- _type = LONG;
- _local_val.long_val = val;
- break;
- }
- case FLOAT: {
- float val = getFloatValue();
- clear_value();
- _type = FLOAT;
- _local_val.float_val = val;
- break;
- }
- case DOUBLE: {
- double val = getDoubleValue();
- clear_value();
- _type = DOUBLE;
- _local_val.double_val = val;
- break;
- }
- case STRING:
- case UNSPECIFIED: {
- string val = getStringValue();
- clear_value();
- _type = STRING;
- _local_val.string_val = copy_string(val.c_str());
- break;
- }
- case NONE:
- default:
- break;
- }
-
- _tied = false;
- return true;
-}
-
-SGPropertyNode *
-SGPropertyNode::getRootNode ()
-{
- if (_parent == 0)
- return this;
- else
- return _parent->getRootNode();
-}
-
-const SGPropertyNode *
-SGPropertyNode::getRootNode () const
-{
- if (_parent == 0)
- return this;
- else
- return _parent->getRootNode();
-}
-
-SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path, bool create)
-{
- if (_path_cache == 0)
- _path_cache = new hash_table;
-
- SGPropertyNode * result = _path_cache->get(relative_path);
- if (result == 0) {
- vector<PathComponent> components;
- parse_path(relative_path, components);
- result = find_node(this, components, 0, create);
- if (result != 0)
- _path_cache->put(relative_path, result);
- }
-
- return result;
-}
-
-SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path, int index, bool create)
-{
- vector<PathComponent> components;
- parse_path(relative_path, components);
- if (components.size() > 0)
- components[components.size()-1].index = index;
- return find_node(this, components, 0, create);
-}
-
-const SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path) const
-{
- return ((SGPropertyNode *)this)->getNode(relative_path, false);
-}
-
-const SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path, int index) const
-{
- return ((SGPropertyNode *)this)->getNode(relative_path, index, false);
-}
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Convenience methods using relative paths.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * Test whether another node has a value attached.
- */
-bool
-SGPropertyNode::hasValue (const char * relative_path) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? false : node->hasValue());
-}
-
-
-/**
- * Get the value type for another node.
- */
-SGPropertyNode::Type
-SGPropertyNode::getType (const char * relative_path) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? UNSPECIFIED : (Type)(node->getType()));
-}
-
-
-/**
- * Get a bool value for another node.
- */
-bool
-SGPropertyNode::getBoolValue (const char * relative_path,
- bool defaultValue) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? defaultValue : node->getBoolValue());
-}
-
-
-/**
- * Get an int value for another node.
- */
-int
-SGPropertyNode::getIntValue (const char * relative_path,
- int defaultValue) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? defaultValue : node->getIntValue());
-}
-
-
-/**
- * Get a long value for another node.
- */
-long
-SGPropertyNode::getLongValue (const char * relative_path,
- long defaultValue) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? defaultValue : node->getLongValue());
-}
-
-
-/**
- * Get a float value for another node.
- */
-float
-SGPropertyNode::getFloatValue (const char * relative_path,
- float defaultValue) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? defaultValue : node->getFloatValue());
-}
-
-
-/**
- * Get a double value for another node.
- */
-double
-SGPropertyNode::getDoubleValue (const char * relative_path,
- double defaultValue) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? defaultValue : node->getDoubleValue());
-}
-
-
-/**
- * Get a string value for another node.
- */
-const char *
-SGPropertyNode::getStringValue (const char * relative_path,
- const char * defaultValue) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? defaultValue : node->getStringValue());
-}
-
-
-/**
- * Set a bool value for another node.
- */
-bool
-SGPropertyNode::setBoolValue (const char * relative_path, bool value)
-{
- return getNode(relative_path, true)->setBoolValue(value);
-}
-
-
-/**
- * Set an int value for another node.
- */
-bool
-SGPropertyNode::setIntValue (const char * relative_path, int value)
-{
- return getNode(relative_path, true)->setIntValue(value);
-}
-
-
-/**
- * Set a long value for another node.
- */
-bool
-SGPropertyNode::setLongValue (const char * relative_path, long value)
-{
- return getNode(relative_path, true)->setLongValue(value);
-}
-
-
-/**
- * Set a float value for another node.
- */
-bool
-SGPropertyNode::setFloatValue (const char * relative_path, float value)
-{
- return getNode(relative_path, true)->setFloatValue(value);
-}
-
-
-/**
- * Set a double value for another node.
- */
-bool
-SGPropertyNode::setDoubleValue (const char * relative_path, double value)
-{
- return getNode(relative_path, true)->setDoubleValue(value);
-}
-
-
-/**
- * Set a string value for another node.
- */
-bool
-SGPropertyNode::setStringValue (const char * relative_path, const char * value)
-{
- return getNode(relative_path, true)->setStringValue(value);
-}
-
-
-/**
- * Set an unknown value for another node.
- */
-bool
-SGPropertyNode::setUnspecifiedValue (const char * relative_path,
- const char * value)
-{
- return getNode(relative_path, true)->setUnspecifiedValue(value);
-}
-
-
-/**
- * Test whether another node is tied.
- */
-bool
-SGPropertyNode::isTied (const char * relative_path) const
-{
- const SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? false : node->isTied());
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
- const SGRawValue<bool> &rawValue,
- bool useDefault)
-{
- return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
- const SGRawValue<int> &rawValue,
- bool useDefault)
-{
- return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * 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.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
- const SGRawValue<float> &rawValue,
- bool useDefault)
-{
- return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
- const SGRawValue<double> &rawValue,
- bool useDefault)
-{
- return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
- const SGRawValue<const char *> &rawValue,
- bool useDefault)
-{
- return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Attempt to untie another node reached by a relative path.
- */
-bool
-SGPropertyNode::untie (const char * relative_path)
-{
- SGPropertyNode * node = getNode(relative_path);
- return (node == 0 ? false : node->untie());
-}
-
-void
-SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener)
-{
- if (_listeners == 0)
- _listeners = new vector<SGPropertyChangeListener *>;
- _listeners->push_back(listener);
- listener->register_property(this);
-}
-
-void
-SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
-{
- vector<SGPropertyChangeListener *>::iterator it =
- find(_listeners->begin(), _listeners->end(), listener);
- if (it != _listeners->end()) {
- _listeners->erase(it);
- listener->unregister_property(this);
- if (_listeners->empty()) {
- vector<SGPropertyChangeListener *> * tmp = _listeners;
- _listeners = 0;
- delete tmp;
- }
- }
-}
-
-void
-SGPropertyNode::fireValueChanged ()
-{
- fireValueChanged(this);
-}
-
-void
-SGPropertyNode::fireChildAdded (SGPropertyNode * child)
-{
- fireChildAdded(this, child);
-}
-
-void
-SGPropertyNode::fireChildRemoved (SGPropertyNode * child)
-{
- fireChildRemoved(this, child);
-}
-
-void
-SGPropertyNode::fireValueChanged (SGPropertyNode * node)
-{
- if (_listeners != 0) {
- for (unsigned int i = 0; i < _listeners->size(); i++) {
- (*_listeners)[i]->valueChanged(node);
- }
- }
- if (_parent != 0)
- _parent->fireValueChanged(node);
-}
-
-void
-SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
- SGPropertyNode * child)
-{
- if (_listeners != 0) {
- for (unsigned int i = 0; i < _listeners->size(); i++) {
- (*_listeners)[i]->childAdded(parent, child);
- }
- }
- if (_parent != 0)
- _parent->fireChildAdded(parent, child);
-}
-
-void
-SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
- SGPropertyNode * child)
-{
- if (_listeners != 0) {
- for (unsigned int i = 0; i < _listeners->size(); i++) {
- (*_listeners)[i]->childRemoved(parent, child);
- }
- }
- if (_parent != 0)
- _parent->fireChildRemoved(parent, child);
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Simplified hash table for caching paths.
-////////////////////////////////////////////////////////////////////////
-
-#define HASH_TABLE_SIZE 199
-
-SGPropertyNode::hash_table::entry::entry ()
- : _key(0),
- _value(0)
-{
-}
-
-SGPropertyNode::hash_table::entry::~entry ()
-{
- // Don't delete the value; we don't own
- // the pointer.
- delete [] _key;
-}
-
-void
-SGPropertyNode::hash_table::entry::set_key (const char * key)
-{
- _key = copy_string(key);
-}
-
-void
-SGPropertyNode::hash_table::entry::set_value (SGPropertyNode * value)
-{
- _value = value;
-}
-
-SGPropertyNode::hash_table::bucket::bucket ()
- : _length(0),
- _entries(0)
-{
-}
-
-SGPropertyNode::hash_table::bucket::~bucket ()
-{
- for (int i = 0; i < _length; i++)
- delete _entries[i];
-}
-
-SGPropertyNode::hash_table::entry *
-SGPropertyNode::hash_table::bucket::get_entry (const char * key, bool create)
-{
- int i;
- for (i = 0; i < _length; i++) {
- if (!strcmp(_entries[i]->get_key(), key))
- return _entries[i];
- }
- if (create) {
- entry ** new_entries = new entry*[_length+1];
- for (i = 0; i < _length; i++) {
- new_entries[i] = _entries[i];
- }
- delete [] _entries;
- _entries = new_entries;
- _entries[_length] = new entry;
- _entries[_length]->set_key(key);
- _length++;
- return _entries[_length - 1];
- } else {
- return 0;
- }
-}
-
-
-SGPropertyNode::hash_table::hash_table ()
- : _data_length(0),
- _data(0)
-{
-}
-
-SGPropertyNode::hash_table::~hash_table ()
-{
- for (unsigned int i = 0; i < _data_length; i++)
- delete _data[i];
-}
-
-SGPropertyNode *
-SGPropertyNode::hash_table::get (const char * key)
-{
- if (_data_length == 0)
- return 0;
- unsigned int index = hashcode(key) % _data_length;
- if (_data[index] == 0)
- return 0;
- entry * e = _data[index]->get_entry(key);
- if (e == 0)
- return 0;
- else
- return e->get_value();
-}
-
-void
-SGPropertyNode::hash_table::put (const char * key, SGPropertyNode * value)
-{
- if (_data_length == 0) {
- _data = new bucket*[HASH_TABLE_SIZE];
- _data_length = HASH_TABLE_SIZE;
- for (unsigned int i = 0; i < HASH_TABLE_SIZE; i++)
- _data[i] = 0;
- }
- unsigned int index = hashcode(key) % _data_length;
- if (_data[index] == 0) {
- _data[index] = new bucket;
- }
- entry * e = _data[index]->get_entry(key, true);
- e->set_value(value);
-}
-
-unsigned int
-SGPropertyNode::hash_table::hashcode (const char * key)
-{
- unsigned int hash = 0;
- while (*key != 0) {
- hash = 31 * hash + *key;
- key++;
- }
- return hash;
-}
-
-
-
-/**
- * Default constructor
- */
-SGPropertyNode_ptr::SGPropertyNode_ptr()
-{
- _ptr = 0;
-}
-
-/**
- * Copy constructor
- */
-SGPropertyNode_ptr::SGPropertyNode_ptr( const SGPropertyNode_ptr &r )
-{
- _ptr = r._ptr;
- if (_ptr)
- _ptr->incrementRef();
-}
-
-/**
- * Constructor from a pointer to a node
- */
-SGPropertyNode_ptr::SGPropertyNode_ptr( SGPropertyNode *p )
-{
- _ptr = p;
- if (_ptr)
- _ptr->incrementRef();
-}
-
-/**
- * Destructor
- */
-SGPropertyNode_ptr::~SGPropertyNode_ptr()
-{
- if (_ptr && _ptr->decrementRef() == 0)
- delete _ptr;
-}
-
-/**
- * Assignement operator
- */
-SGPropertyNode_ptr &
-SGPropertyNode_ptr::operator=( const SGPropertyNode_ptr &r )
-{
- if (_ptr && _ptr->decrementRef() == 0)
- delete _ptr;
- _ptr = r._ptr;
- if (_ptr)
- _ptr->incrementRef();
-
- return *this;
-}
-
-/**
- * Pointer access operator
- */
-SGPropertyNode *
-SGPropertyNode_ptr::operator->()
-{
- return _ptr;
-}
-
-/**
- * Pointer access operator (const)
- */
-const SGPropertyNode *
-SGPropertyNode_ptr::operator->() const
-{
- return _ptr;
-}
-
-/**
- * Conversion to SGPropertyNode * operator
- */
-SGPropertyNode_ptr::operator SGPropertyNode *()
-{
- return _ptr;
-}
-
-/**
- * Conversion to const SGPropertyNode * operator
- */
-SGPropertyNode_ptr::operator const SGPropertyNode *() const
-{
- return _ptr;
-}
-
-/**
- * Validity test
- */
-bool
-SGPropertyNode_ptr::valid() const
-{
- return _ptr != 0;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGPropertyChangeListener.
-////////////////////////////////////////////////////////////////////////
-
-SGPropertyChangeListener::~SGPropertyChangeListener ()
-{
- // This will come back and remove
- // the current item each time. Is
- // that OK?
- vector<SGPropertyNode *>::iterator it;
- for (it = _properties.begin(); it != _properties.end(); it++)
- (*it)->removeChangeListener(this);
-}
-
-void
-SGPropertyChangeListener::valueChanged (SGPropertyNode * node)
-{
- // NO-OP
-}
-
-void
-SGPropertyChangeListener::childAdded (SGPropertyNode * node,
- SGPropertyNode * child)
-{
- // NO-OP
-}
-
-void
-SGPropertyChangeListener::childRemoved (SGPropertyNode * parent,
- SGPropertyNode * child)
-{
- // NO-OP
-}
-
-void
-SGPropertyChangeListener::register_property (SGPropertyNode * node)
-{
- _properties.push_back(node);
-}
-
-void
-SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
-{
- vector<SGPropertyNode *>::iterator it =
- find(_properties.begin(), _properties.end(), node);
- if (it != _properties.end())
- _properties.erase(it);
-}
-
-
-// end of props.cxx
+++ /dev/null
-/**
- * \file props.hxx
- * Interface definition for a property list.
- * Started Fall 2000 by David Megginson, david@megginson.com
- * This code is released into the Public Domain.
- *
- * See props.html for documentation [replace with URL when available].
- *
- * $Id$
- */
-
-#ifndef __PROPS_HXX
-#define __PROPS_HXX
-
-#ifndef PROPS_STANDALONE
-#define PROPS_STANDALONE 0
-#endif
-
-#include <vector>
-
-#if PROPS_STANDALONE
-
-#include <string>
-#include <iostream>
-
-using std::string;
-using std::vector;
-using std::istream;
-using std::ostream;
-
-#else
-
-#include <simgear/compiler.h>
-#include <simgear/debug/logstream.hxx>
-#include STL_STRING
-#include STL_IOSTREAM
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(istream);
-SG_USING_STD(ostream);
-
-#endif
-
-
-#ifdef NONE
-#pragma warn A sloppy coder has defined NONE as a macro!
-#undef NONE
-#endif
-
-#ifdef ALIAS
-#pragma warn A sloppy coder has defined ALIAS as a macro!
-#undef ALIAS
-#endif
-
-#ifdef UNSPECIFIED
-#pragma warn A sloppy coder has defined UNSPECIFIED as a macro!
-#undef UNSPECIFIED
-#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 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
-#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
-////////////////////////////////////////////////////////////////////////
-// A raw value.
-//
-// This is the mechanism that information-providing routines can
-// use to link their own values to the property manager. Any
-// SGValue can be tied to a raw value and then untied again.
-//
-// Note: we are forced to use inlined methods here to ensure
-// that the templates will be instantiated. We're probably taking
-// a small performance hit for that.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * Abstract base class for a raw value.
- *
- * <p>The property manager is implemented in two layers. The {@link
- * SGPropertyNode} is the highest and most abstract layer,
- * representing an LValue/RValue pair: it records the position of the
- * property in the property tree and contains facilities for
- * navigation to other nodes. It is guaranteed to be persistent: the
- * {@link SGPropertyNode} will not change during a session, even if
- * the property is bound and unbound multiple times.</p>
- *
- * <p>When the property value is not managed internally in the
- * SGPropertyNode, the SGPropertyNode will contain a reference to an
- * SGRawValue (this class), which provides an abstract way to get,
- * set, and clone the underlying value. The SGRawValue may change
- * frequently during a session as a value is retyped or bound and
- * unbound to various data source, but the abstract SGPropertyNode
- * layer insulates the application from those changes. The raw value
- * contains no facilities for data binding or for type conversion: it
- * is simply the abstraction of a primitive data type (or a compound
- * data type, in the case of a string).</p>
- *
- * <p>The SGPropertyNode class always keeps a *copy* of a raw value,
- * not the original one passed to it; if you override a derived class
- * but do not replace the {@link #clone} method, strange things will
- * happen.</p>
- *
- * <p>All derived SGRawValue classes must implement {@link #getValue},
- * {@link #setValue}, and {@link #clone} for the appropriate type.</p>
- *
- * @see SGPropertyNode
- * @see SGRawValuePointer
- * @see SGRawValueFunctions
- * @see SGRawValueFunctionsIndexed
- * @see SGRawValueMethods
- * @see SGRawValueMethodsIndexed
- */
-template <class T>
-class SGRawValue
-{
-public:
-
- /**
- * The default underlying value for this type.
- *
- * Every raw value has a default; the default is false for a
- * boolean, 0 for the various numeric values, and "" for a string.
- * If additional types of raw values are added in the future, they
- * may need different kinds of default values (such as epoch for a
- * date type). The default value is used when creating new values.
- */
- static const T DefaultValue; // Default for this kind of raw value.
-
-
- /**
- * Constructor.
- *
- * Use the default value for this type.
- */
- SGRawValue () {}
-
-
- /**
- * Destructor.
- */
- virtual ~SGRawValue () {}
-
-
- /**
- * Return the underlying value.
- *
- * @return The actual value for the property.
- * @see #setValue
- */
- virtual T getValue () const = 0;
-
-
- /**
- * Assign a new underlying value.
- *
- * If the new value cannot be set (because this is a read-only
- * raw value, or because the new value is not acceptable for
- * some reason) this method returns false and leaves the original
- * value unchanged.
- *
- * @param value The actual value for the property.
- * @return true if the value was set successfully, false otherwise.
- * @see #getValue
- */
- virtual bool setValue (T value) = 0;
-
-
- /**
- * Create a new deep copy of this raw value.
- *
- * The copy will contain its own version of the underlying value
- * as well.
- *
- * @return A deep copy of the current object.
- */
- virtual SGRawValue * clone () const = 0;
-};
-
-
-/**
- * A raw value bound to a pointer.
- *
- * This is the most efficient way to tie an external value, but also
- * the most dangerous, because there is no way for the supplier to
- * perform bounds checking and derived calculations except by polling
- * the variable to see if it has changed. There is no default
- * constructor, because this class would be meaningless without a
- * pointer.
- */
-template <class T>
-class SGRawValuePointer : public SGRawValue<T>
-{
-public:
-
- /**
- * Explicit pointer constructor.
- *
- * Create a new raw value bound to the value of the variable
- * referenced by the pointer.
- *
- * @param ptr The pointer to the variable to which this raw value
- * will be bound.
- */
- SGRawValuePointer (T * ptr) : _ptr(ptr) {}
-
- /**
- * Destructor.
- */
- virtual ~SGRawValuePointer () {}
-
- /**
- * Get the underlying value.
- *
- * This method will dereference the pointer and return the
- * variable's value.
- */
- virtual T getValue () const { return *_ptr; }
-
- /**
- * Set the underlying value.
- *
- * This method will dereference the pointer and change the
- * variable's value.
- */
- virtual bool setValue (T value) { *_ptr = value; return true; }
-
- /**
- * Create a copy of this raw value.
- *
- * The copy will use the same external pointer as the original.
- */
- virtual SGRawValue<T> * clone () const {
- return new SGRawValuePointer<T>(_ptr);
- }
-
-private:
- T * _ptr;
-};
-
-
-/**
- * A value managed through static functions.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.
- */
-template <class T>
-class SGRawValueFunctions : public SGRawValue<T>
-{
-public:
-
- /**
- * The template type of a static getter function.
- */
- typedef T (*getter_t)();
-
- /**
- * The template type of a static setter function.
- */
- typedef void (*setter_t)(T);
-
- /**
- * Explicit constructor.
- *
- * Create a new raw value bound to the getter and setter supplied.
- *
- * @param getter A static function for getting a value, or 0
- * to read-disable the value.
- * @param setter A static function for setting a value, or 0
- * to write-disable the value.
- */
- SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0)
- : _getter(getter), _setter(setter) {}
-
- /**
- * Destructor.
- */
- virtual ~SGRawValueFunctions () {}
-
- /**
- * Get the underlying value.
- *
- * This method will invoke the getter function to get a value.
- * If no getter function was supplied, this method will always
- * return the default value for the type.
- */
- virtual T getValue () const {
- if (_getter) return (*_getter)();
- else return SGRawValue<T>::DefaultValue;
- }
-
- /**
- * Set the underlying value.
- *
- * This method will invoke the setter function to change the
- * underlying value. If no setter function was supplied, this
- * method will return false.
- */
- virtual bool setValue (T value) {
- if (_setter) { (*_setter)(value); return true; }
- else return false;
- }
-
- /**
- * Create a copy of this raw value, bound to the same functions.
- */
- virtual SGRawValue<T> * clone () const {
- return new SGRawValueFunctions<T>(_getter,_setter);
- }
-
-private:
- getter_t _getter;
- setter_t _setter;
-};
-
-
-/**
- * An indexed value bound to static functions.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter. An indexed value is useful for binding one
- * of a list of possible values (such as multiple engines for a
- * plane). The index is hard-coded at creation time.
- *
- * @see SGRawValue
- */
-template <class T>
-class SGRawValueFunctionsIndexed : public SGRawValue<T>
-{
-public:
- typedef T (*getter_t)(int);
- typedef void (*setter_t)(int,T);
- SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0)
- : _index(index), _getter(getter), _setter(setter) {}
- virtual ~SGRawValueFunctionsIndexed () {}
- virtual T getValue () const {
- if (_getter) return (*_getter)(_index);
- else return SGRawValue<T>::DefaultValue;
- }
- virtual bool setValue (T value) {
- if (_setter) { (*_setter)(_index, value); return true; }
- else return false;
- }
- virtual SGRawValue<T> * clone () const {
- return new SGRawValueFunctionsIndexed<T>(_index, _getter, _setter);
- }
-private:
- int _index;
- getter_t _getter;
- setter_t _setter;
-};
-
-
-/**
- * A value managed through an object and access methods.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.
- */
-template <class C, class T>
-class SGRawValueMethods : public SGRawValue<T>
-{
-public:
- typedef T (C::*getter_t)() const;
- typedef void (C::*setter_t)(T);
- SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0)
- : _obj(obj), _getter(getter), _setter(setter) {}
- virtual ~SGRawValueMethods () {}
- virtual T getValue () const {
- if (_getter) { return (_obj.*_getter)(); }
- else { return SGRawValue<T>::DefaultValue; }
- }
- virtual bool setValue (T value) {
- if (_setter) { (_obj.*_setter)(value); return true; }
- else return false;
- }
- virtual SGRawValue<T> * clone () const {
- return new SGRawValueMethods<C,T>(_obj, _getter, _setter);
- }
-private:
- C &_obj;
- getter_t _getter;
- setter_t _setter;
-};
-
-
-/**
- * An indexed value managed through an object and access methods.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.
- */
-template <class C, class T>
-class SGRawValueMethodsIndexed : public SGRawValue<T>
-{
-public:
- typedef T (C::*getter_t)(int) const;
- typedef void (C::*setter_t)(int, T);
- SGRawValueMethodsIndexed (C &obj, int index,
- getter_t getter = 0, setter_t setter = 0)
- : _obj(obj), _index(index), _getter(getter), _setter(setter) {}
- virtual ~SGRawValueMethodsIndexed () {}
- virtual T getValue () const {
- if (_getter) { return (_obj.*_getter)(_index); }
- else { return SGRawValue<T>::DefaultValue; }
- }
- virtual bool setValue (T value) {
- if (_setter) { (_obj.*_setter)(_index, value); return true; }
- else return false;
- }
- virtual SGRawValue<T> * clone () const {
- return new SGRawValueMethodsIndexed<C,T>(_obj, _index, _getter, _setter);
- }
-private:
- C &_obj;
- int _index;
- getter_t _getter;
- setter_t _setter;
-};
-
-\f
-/**
- * The smart pointer that manage reference counting
- */
-class SGPropertyNode;
-class SGPropertyNode_ptr
-{
-public:
-
- /**
- * Default constructor
- */
- SGPropertyNode_ptr();
-
- /**
- * Copy constructor
- */
- SGPropertyNode_ptr( const SGPropertyNode_ptr &r );
-
- /**
- * Constructor from a pointer to a node
- */
- SGPropertyNode_ptr( SGPropertyNode *p );
-
- /**
- * Destructor
- */
- ~SGPropertyNode_ptr();
-
- /**
- * Assignement operator
- */
- SGPropertyNode_ptr &operator=( const SGPropertyNode_ptr &r );
-
- /**
- * Pointer access operator
- */
- SGPropertyNode *operator->();
-
- /**
- * Pointer access operator (const)
- */
- const SGPropertyNode *operator->() const;
-
- /**
- * Conversion to SGPropertyNode * operator
- */
- operator SGPropertyNode *();
-
- /**
- * Conversion to const SGPropertyNode * operator
- */
- operator const SGPropertyNode *() const;
-
- /**
- * Return the pointer.
- */
- SGPropertyNode * ptr () { return _ptr; }
-
- /**
- * Validity test
- */
- bool valid() const;
-
-private:
-
- SGPropertyNode *_ptr;
-};
-
-
-\f
-/**
- * The property change listener interface.
- *
- * <p>Any class that needs to listen for property changes must implement
- * this interface.</p>
- */
-class SGPropertyChangeListener
-{
-public:
- virtual ~SGPropertyChangeListener ();
- virtual void valueChanged (SGPropertyNode * node);
- virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child);
- virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child);
-
-protected:
- friend class SGPropertyNode;
- virtual void register_property (SGPropertyNode * node);
- virtual void unregister_property (SGPropertyNode * node);
-
-private:
- vector<SGPropertyNode *> _properties;
-};
-
-
-\f
-/**
- * A node in a property tree.
- */
-class SGPropertyNode
-{
-public:
-
- /**
- * Public constants.
- */
- enum {
- MAX_STRING_LEN = 1024
- };
-
- /**
- * Property value types.
- */
- enum Type {
- NONE,
- ALIAS,
- BOOL,
- INT,
- LONG,
- FLOAT,
- DOUBLE,
- STRING,
- UNSPECIFIED
- };
-
-
- /**
- * Access mode attributes.
- *
- * <p>The ARCHIVE attribute is strictly advisory, and controls
- * whether the property should normally be saved and restored.</p>
- */
- enum Attribute {
- READ = 1,
- WRITE = 2,
- ARCHIVE = 4,
- REMOVED = 8,
- TRACE_READ = 16,
- TRACE_WRITE = 32
- };
-
-
- /**
- * Last used attribute
- * Update as needed when enum Attribute is changed
- */
- static const int LAST_USED_ATTRIBUTE;
-
-
- /**
- * Default constructor.
- */
- SGPropertyNode ();
-
-
- /**
- * Copy constructor.
- */
- SGPropertyNode (const SGPropertyNode &node);
-
-
- /**
- * Destructor.
- */
- virtual ~SGPropertyNode ();
-
-
-
- //
- // Basic properties.
- //
-
- /**
- * Test whether this node contains a primitive leaf value.
- */
- bool hasValue () const { return (_type != NONE); }
-
-
- /**
- * Get the node's simple (XML) name.
- */
- const char * getName () const { return _name; }
-
-
- /**
- * Get the node's pretty display name, with subscript when needed.
- */
- const char * getDisplayName (bool simplify = false) const;
-
-
- /**
- * Get the node's integer index.
- */
- int getIndex () const { return _index; }
-
-
- /**
- * Get a non-const pointer to the node's parent.
- */
- SGPropertyNode * getParent () { return _parent; }
-
-
- /**
- * Get a const pointer to the node's parent.
- */
- const SGPropertyNode * getParent () const { return _parent; }
-
-
- //
- // Children.
- //
-
-
- /**
- * Get the number of child nodes.
- */
- int nChildren () const { return _children.size(); }
-
-
- /**
- * Get a child node by position (*NOT* index).
- */
- SGPropertyNode * getChild (int position);
-
-
- /**
- * Get a const child node by position (*NOT* index).
- */
- const SGPropertyNode * getChild (int position) const;
-
-
- /**
- * Test whether a named child exists.
- */
- bool hasChild (const char * name, int index = 0) const
- {
- return (getChild(name, index) != 0);
- }
-
-
- /**
- * Get a child node by name and index.
- */
- SGPropertyNode * getChild (const char * name, int index = 0,
- bool create = false);
-
-
- /**
- * Get a const child node by name and index.
- */
- const SGPropertyNode * getChild (const char * name, int index = 0) const;
-
-
- /**
- * Get a vector of all children with the specified name.
- */
- vector<SGPropertyNode_ptr> getChildren (const char * name) const;
-
-
- /**
- * Remove a child node
- */
- SGPropertyNode_ptr removeChild (const char * name, int index = 0,
- bool keep = true);
-
-
- //
- // Alias support.
- //
-
-
- /**
- * Alias this node's leaf value to another's.
- */
- bool alias (SGPropertyNode * target);
-
-
- /**
- * Alias this node's leaf value to another's by relative path.
- */
- bool alias (const char * path);
-
-
- /**
- * Remove any alias for this node.
- */
- bool unalias ();
-
-
- /**
- * Test whether the node's leaf value is aliased to another's.
- */
- bool isAlias () const { return (_type == ALIAS); }
-
-
- /**
- * Get a non-const pointer to the current alias target, if any.
- */
- SGPropertyNode * getAliasTarget ();
-
-
- /**
- * Get a const pointer to the current alias target, if any.
- */
- const SGPropertyNode * getAliasTarget () const;
-
-
- //
- // Path information.
- //
-
-
- /**
- * Get the path to this node from the root.
- */
- const char * getPath (bool simplify = false) const;
-
-
- /**
- * Get a pointer to the root node.
- */
- SGPropertyNode * getRootNode ();
-
-
- /**
- * Get a const pointer to the root node.
- */
- const SGPropertyNode * getRootNode () const;
-
-
- /**
- * Get a pointer to another node by relative path.
- */
- SGPropertyNode * getNode (const char * relative_path, bool create = false);
-
-
- /**
- * Get a pointer to another node by relative path.
- *
- * This method leaves the index off the last member of the path,
- * so that the user can specify it separately (and save some
- * string building). For example, getNode("/bar[1]/foo", 3) is
- * exactly equivalent to getNode("bar[1]/foo[3]"). The index
- * provided overrides any given in the path itself for the last
- * component.
- */
- SGPropertyNode * getNode (const char * relative_path, int index,
- bool create = false);
-
-
- /**
- * Get a const pointer to another node by relative path.
- */
- const SGPropertyNode * getNode (const char * relative_path) const;
-
-
- /**
- * Get a const pointer to another node by relative path.
- *
- * This method leaves the index off the last member of the path,
- * so that the user can specify it separate.
- */
- const SGPropertyNode * getNode (const char * relative_path,
- int index) const;
-
-
- //
- // Access Mode.
- //
-
- /**
- * Check a single mode attribute for the property node.
- */
- bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); }
-
-
- /**
- * Set a single mode attribute for the property node.
- */
- void setAttribute (Attribute attr, bool state) {
- (state ? _attr |= attr : _attr &= ~attr);
- }
-
-
- /**
- * Get all of the mode attributes for the property node.
- */
- int getAttributes () const { return _attr; }
-
-
- /**
- * Set all of the mode attributes for the property node.
- */
- void setAttributes (int attr) { _attr = attr; }
-
-
- //
- // Leaf Value (primitive).
- //
-
-
- /**
- * Get the type of leaf value, if any, for this node.
- */
- Type getType () const;
-
-
- /**
- * Get a bool value for this node.
- */
- bool getBoolValue () const;
-
-
- /**
- * Get an int value for this node.
- */
- int getIntValue () const;
-
-
- /**
- * Get a long int value for this node.
- */
- long getLongValue () const;
-
-
- /**
- * Get a float value for this node.
- */
- float getFloatValue () const;
-
-
- /**
- * Get a double value for this node.
- */
- double getDoubleValue () const;
-
-
- /**
- * Get a string value for this node.
- */
- const char * getStringValue () const;
-
-
-
- /**
- * Set a bool value for this node.
- */
- bool setBoolValue (bool value);
-
-
- /**
- * Set an int value for this node.
- */
- bool setIntValue (int value);
-
-
- /**
- * Set a long int value for this node.
- */
- bool setLongValue (long value);
-
-
- /**
- * Set a float value for this node.
- */
- bool setFloatValue (float value);
-
-
- /**
- * Set a double value for this node.
- */
- bool setDoubleValue (double value);
-
-
- /**
- * Set a string value for this node.
- */
- bool setStringValue (const char * value);
-
-
- /**
- * Set a value of unspecified type for this node.
- */
- bool setUnspecifiedValue (const char * value);
-
-
- //
- // Data binding.
- //
-
-
- /**
- * Test whether this node is bound to an external data source.
- */
- bool isTied () const { return _tied; }
-
-
- /**
- * Bind this node to an external bool source.
- */
- bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
-
-
- /**
- * Bind this node to an external int source.
- */
- bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
-
-
- /**
- * Bind this node to an external long int source.
- */
- bool tie (const SGRawValue<long> &rawValue, bool useDefault = true);
-
-
- /**
- * Bind this node to an external float source.
- */
- bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
-
-
- /**
- * Bind this node to an external double source.
- */
- bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
-
-
- /**
- * Bind this node to an external string source.
- */
- bool tie (const SGRawValue<const char *> &rawValue, bool useDefault = true);
-
-
- /**
- * Unbind this node from any external data source.
- */
- bool untie ();
-
-
- //
- // Convenience methods using paths.
- // TODO: add attribute methods
- //
-
-
- /**
- * Get another node's type.
- */
- Type getType (const char * relative_path) const;
-
-
- /**
- * Test whether another node has a leaf value.
- */
- bool hasValue (const char * relative_path) const;
-
-
- /**
- * Get another node's value as a bool.
- */
- bool getBoolValue (const char * relative_path,
- bool defaultValue = false) const;
-
-
- /**
- * Get another node's value as an int.
- */
- int getIntValue (const char * relative_path,
- int defaultValue = 0) const;
-
-
- /**
- * Get another node's value as a long int.
- */
- long getLongValue (const char * relative_path,
- long defaultValue = 0L) const;
-
-
- /**
- * Get another node's value as a float.
- */
- float getFloatValue (const char * relative_path,
- float defaultValue = 0.0) const;
-
-
- /**
- * Get another node's value as a double.
- */
- double getDoubleValue (const char * relative_path,
- double defaultValue = 0.0L) const;
-
-
- /**
- * Get another node's value as a string.
- */
- const char * getStringValue (const char * relative_path,
- const char * defaultValue = "") const;
-
-
- /**
- * Set another node's value as a bool.
- */
- bool setBoolValue (const char * relative_path, bool value);
-
-
- /**
- * Set another node's value as an int.
- */
- bool setIntValue (const char * relative_path, int value);
-
-
- /**
- * Set another node's value as a long int.
- */
- bool setLongValue (const char * relative_path, long value);
-
-
- /**
- * Set another node's value as a float.
- */
- bool setFloatValue (const char * relative_path, float value);
-
-
- /**
- * Set another node's value as a double.
- */
- bool setDoubleValue (const char * relative_path, double value);
-
-
- /**
- * Set another node's value as a string.
- */
- bool setStringValue (const char * relative_path, const char * value);
-
-
- /**
- * Set another node's value with no specified type.
- */
- bool setUnspecifiedValue (const char * relative_path, const char * value);
-
-
- /**
- * Test whether another node is bound to an external data source.
- */
- bool isTied (const char * relative_path) const;
-
-
- /**
- * Bind another node to an external bool source.
- */
- bool tie (const char * relative_path, const SGRawValue<bool> &rawValue,
- bool useDefault = true);
-
-
- /**
- * Bind another node to an external int source.
- */
- bool tie (const char * relative_path, const SGRawValue<int> &rawValue,
- bool useDefault = true);
-
-
- /**
- * Bind another node to an external long int source.
- */
- bool tie (const char * relative_path, const SGRawValue<long> &rawValue,
- bool useDefault = true);
-
-
- /**
- * Bind another node to an external float source.
- */
- bool tie (const char * relative_path, const SGRawValue<float> &rawValue,
- bool useDefault = true);
-
-
- /**
- * Bind another node to an external double source.
- */
- bool tie (const char * relative_path, const SGRawValue<double> &rawValue,
- bool useDefault = true);
-
-
- /**
- * Bind another node to an external string source.
- */
- bool tie (const char * relative_path, const SGRawValue<const char *> &rawValue,
- bool useDefault = true);
-
-
- /**
- * Unbind another node from any external data source.
- */
- bool untie (const char * relative_path);
-
-
- /**
- * Add a change listener to the property.
- */
- void addChangeListener (SGPropertyChangeListener * listener);
-
-
- /**
- * Remove a change listener from the property.
- */
- void removeChangeListener (SGPropertyChangeListener * listener);
-
-
- /**
- * Fire a value change event to all listeners.
- */
- void fireValueChanged ();
-
-
- /**
- * Fire a child-added event to all listeners.
- */
- void fireChildAdded (SGPropertyNode * child);
-
-
- /**
- * Fire a child-removed event to all listeners.
- */
- void fireChildRemoved (SGPropertyNode * child);
-
-
-protected:
-
- void fireValueChanged (SGPropertyNode * node);
- void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child);
- void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child);
-
- /**
- * Protected constructor for making new nodes on demand.
- */
- SGPropertyNode (const char * name, int index, SGPropertyNode * parent);
-
-
-private:
-
- // Get the raw value
- bool get_bool () const;
- int get_int () const;
- long get_long () const;
- float get_float () const;
- double get_double () const;
- const char * get_string () const;
-
- // Set the raw value
- bool set_bool (bool value);
- bool set_int (int value);
- bool set_long (long value);
- bool set_float (float value);
- bool set_double (double value);
- bool set_string (const char * value);
-
-
- /**
- * Clear any existing value and set the type to NONE.
- */
- void clear_value ();
-
-
- /**
- * Get the value as a string.
- */
- const char * make_string () const;
-
-
- /**
- * Trace a read access.
- */
- void trace_read () const;
-
-
- /**
- * Trace a write access.
- */
- void trace_write () const;
-
-
- /**
- * Increment reference counter
- */
- void incrementRef();
-
- /**
- * Decrement reference counter
- */
- int decrementRef();
-
- friend class SGPropertyNode_ptr;
-
-
- mutable char _buffer[MAX_STRING_LEN+1];
-
- class hash_table;
-
- char * _name;
- mutable char * _display_name;
- int _index;
- SGPropertyNode * _parent;
- vector<SGPropertyNode_ptr> _children;
- vector<SGPropertyNode_ptr> _removedChildren;
- mutable char * _path;
- hash_table * _path_cache;
- Type _type;
- bool _tied;
- int _attr;
- int _count;
-
- // The right kind of pointer...
- union {
- SGPropertyNode * alias;
- SGRawValue<bool> * bool_val;
- SGRawValue<int> * int_val;
- SGRawValue<long> * long_val;
- SGRawValue<float> * float_val;
- SGRawValue<double> * double_val;
- SGRawValue<const char *> * string_val;
- } _value;
-
- union {
- bool bool_val;
- int int_val;
- long long_val;
- float float_val;
- double double_val;
- char * string_val;
- } _local_val;
-
- vector <SGPropertyChangeListener *> * _listeners;
-
-
-\f
- /**
- * A very simple hash table with no remove functionality.
- */
- class hash_table {
- public:
-
- /**
- * An entry in a bucket in a hash table.
- */
- class entry {
- public:
- entry ();
- virtual ~entry ();
- virtual const char * get_key () { return _key; }
- virtual void set_key (const char * key);
- virtual SGPropertyNode * get_value () { return _value; }
- virtual void set_value (SGPropertyNode * value);
- private:
- char * _key;
- SGPropertyNode * _value;
- };
-
-
- /**
- * A bucket in a hash table.
- */
- class bucket {
- public:
- bucket ();
- virtual ~bucket ();
- virtual entry * get_entry (const char * key, bool create = false);
- private:
- int _length;
- entry ** _entries;
- };
-
- friend class bucket;
-
- hash_table ();
- virtual ~hash_table ();
- virtual SGPropertyNode * get (const char * key);
- virtual void put (const char * key, SGPropertyNode * value);
-
- private:
- unsigned int hashcode (const char * key);
- unsigned int _data_length;
- bucket ** _data;
- };
-
-};
-
-#endif // __PROPS_HXX
-
-// end of props.hxx
+++ /dev/null
-
-#include <simgear/compiler.h>
-
-#include <stdlib.h> // atof() atoi()
-
-#include <simgear/sg_inlines.h>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/xml/easyxml.hxx>
-
-#include "sg_path.hxx"
-#include "props.hxx"
-#include "props_io.hxx"
-
-#include STL_IOSTREAM
-#include STL_FSTREAM
-#include STL_STRING
-#include <vector>
-#include <map>
-
-SG_USING_STD(istream);
-SG_USING_STD(ifstream);
-SG_USING_STD(ostream);
-SG_USING_STD(ofstream);
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(map);
-
-#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Property list visitor, for XML parsing.
-////////////////////////////////////////////////////////////////////////
-
-class PropsVisitor : public XMLVisitor
-{
-public:
-
- PropsVisitor (SGPropertyNode * root, const string &base)
- : _root(root), _level(0), _base(base), _hasException(false) {}
-
- virtual ~PropsVisitor () {}
-
- void startXML ();
- void endXML ();
- 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 column);
-
- bool hasException () const { return _hasException; }
- sg_io_exception &getException () { return _exception; }
- void setException (const sg_io_exception &exception) {
- _exception = exception;
- _hasException = true;
- }
-
-private:
-
- struct State
- {
- State () : node(0), type(""), mode(DEFAULT_MODE) {}
- State (SGPropertyNode * _node, const char * _type, int _mode)
- : node(_node), type(_type), mode(_mode) {}
- SGPropertyNode * node;
- string type;
- int mode;
- map<string,int> counters;
- };
-
- State &state () { return _state_stack[_state_stack.size() - 1]; }
-
- void push_state (SGPropertyNode * node, const char * type, int mode) {
- if (type == 0)
- _state_stack.push_back(State(node, "unspecified", mode));
- else
- _state_stack.push_back(State(node, type, mode));
- _level++;
- _data = "";
- }
-
- void pop_state () {
- _state_stack.pop_back();
- _level--;
- }
-
- string _data;
- SGPropertyNode * _root;
- int _level;
- vector<State> _state_stack;
- string _base;
- sg_io_exception _exception;
- bool _hasException;
-};
-
-void
-PropsVisitor::startXML ()
-{
- _level = 0;
- _state_stack.resize(0);
-}
-
-void
-PropsVisitor::endXML ()
-{
- _level = 0;
- _state_stack.resize(0);
-}
-
-
-/**
- * Check a yes/no flag, with default.
- */
-static bool
-checkFlag (const char * flag, bool defaultState = true)
-{
- if (flag == 0)
- return defaultState;
- else if (string(flag) == "y")
- return true;
- else if (string(flag) == "n")
- return false;
- else {
- string message = "Unrecognized flag value '";
- message += flag;
- message += '\'';
- // FIXME: add location info
- throw sg_io_exception(message, "SimGear Property Reader");
- }
-}
-
-void
-PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
-{
- State &st = state();
- const char * attval;
-
- if (_level == 0) {
- if (string(name) != (string)"PropertyList") {
- string message = "Root element name is ";
- message += name;
- message += "; expected PropertyList";
- throw sg_io_exception(message, "SimGear Property Reader");
- }
-
- // Check for an include.
- attval = atts.getValue("include");
- if (attval != 0) {
- SGPath path(SGPath(_base).dir());
- path.append(attval);
- try {
- readProperties(path.str(), _root);
- } catch (sg_io_exception &e) {
- setException(e);
- }
- }
-
- push_state(_root, "", DEFAULT_MODE);
- }
-
- else {
- // Get the index.
- attval = atts.getValue("n");
- int index = 0;
- if (attval != 0) {
- index = atoi(attval);
- st.counters[name] = SG_MAX2(st.counters[name], index+1);
- } else {
- index = st.counters[name];
- st.counters[name]++;
- }
-
- // Got the index, so grab the node.
- SGPropertyNode * node = st.node->getChild(name, index, true);
-
- // Get the access-mode attributes,
- // but don't set yet (in case they
- // prevent us from recording the value).
- int mode = 0;
-
- attval = atts.getValue("read");
- if (checkFlag(attval, true))
- mode |= SGPropertyNode::READ;
- attval = atts.getValue("write");
- if (checkFlag(attval, true))
- mode |= SGPropertyNode::WRITE;
- attval = atts.getValue("archive");
- if (checkFlag(attval, false))
- mode |= SGPropertyNode::ARCHIVE;
- attval = atts.getValue("trace-read");
- if (checkFlag(attval, false))
- mode |= SGPropertyNode::TRACE_READ;
- attval = atts.getValue("trace-write");
- if (checkFlag(attval, false))
- mode |= SGPropertyNode::TRACE_WRITE;
-
- // Check for an alias.
- attval = atts.getValue("alias");
- if (attval != 0) {
- if (!node->alias(attval))
- SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
- }
-
- // Check for an include.
- attval = atts.getValue("include");
- if (attval != 0) {
- SGPath path(SGPath(_base).dir());
- path.append(attval);
- try {
- readProperties(path.str(), node);
- } catch (sg_io_exception &e) {
- setException(e);
- }
- }
-
- push_state(node, atts.getValue("type"), mode);
- }
-}
-
-void
-PropsVisitor::endElement (const char * name)
-{
- State &st = state();
- bool ret;
-
- // 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);
- else
- ret = st.node->setBoolValue(false);
- } else if (st.type == "int") {
- ret = st.node->setIntValue(atoi(_data.c_str()));
- } else if (st.type == "long") {
- ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
- } else if (st.type == "float") {
- ret = st.node->setFloatValue(atof(_data.c_str()));
- } else if (st.type == "double") {
- ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
- } else if (st.type == "string") {
- ret = st.node->setStringValue(_data.c_str());
- } else if (st.type == "unspecified") {
- ret = st.node->setUnspecifiedValue(_data.c_str());
- } else {
- string message = "Unrecognized data type '";
- message += st.type;
- message += '\'';
- // FIXME: add location information
- throw sg_io_exception(message, "SimGear Property Reader");
- }
- if (!ret)
- SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
- << st.node->getPath() << " to value \""
- << _data << "\" with type " << st.type);
- }
-
- // Set the access-mode attributes now,
- // once the value has already been
- // assigned.
- st.node->setAttributes(st.mode);
-
- pop_state();
-}
-
-void
-PropsVisitor::data (const char * s, int length)
-{
- if (state().node->nChildren() == 0)
- _data.append(string(s, length));
-}
-
-void
-PropsVisitor::warning (const char * message, int line, int column)
-{
- SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
- << message << " at line " << line << ", column " << column);
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// 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.
- */
-void
-readProperties (istream &input, SGPropertyNode * start_node,
- const string &base)
-{
- PropsVisitor visitor(start_node, base);
- readXML(input, visitor, base);
- if (visitor.hasException())
- throw visitor.getException();
-}
-
-
-/**
- * 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.
- */
-void
-readProperties (const string &file, SGPropertyNode * start_node)
-{
- PropsVisitor visitor(start_node, file);
- readXML(file, visitor);
- if (visitor.hasException())
- throw visitor.getException();
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Property list writer.
-////////////////////////////////////////////////////////////////////////
-
-#define INDENT_STEP 2
-
-/**
- * Return the type name.
- */
-static const char *
-getTypeName (SGPropertyNode::Type type)
-{
- switch (type) {
- case SGPropertyNode::UNSPECIFIED:
- return "unspecified";
- case SGPropertyNode::BOOL:
- return "bool";
- case SGPropertyNode::INT:
- return "int";
- case SGPropertyNode::LONG:
- return "long";
- case SGPropertyNode::FLOAT:
- return "float";
- case SGPropertyNode::DOUBLE:
- return "double";
- case SGPropertyNode::STRING:
- return "string";
- case SGPropertyNode::ALIAS:
- case SGPropertyNode::NONE:
- return "unspecified";
- }
-
- // keep the compiler from squawking
- return "unspecified";
-}
-
-
-/**
- * Escape characters for output.
- */
-static void
-writeData (ostream &output, const string &data)
-{
- for (int i = 0; i < (int)data.size(); i++) {
- switch (data[i]) {
- case '&':
- output << "&";
- break;
- case '<':
- output << "<";
- break;
- case '>':
- output << ">";
- break;
- default:
- output << data[i];
- break;
- }
- }
-}
-
-static void
-doIndent (ostream &output, int indent)
-{
- while (indent-- > 0) {
- output << ' ';
- }
-}
-
-
-static void
-writeAtts (ostream &output, const SGPropertyNode * node)
-{
- int index = node->getIndex();
-
- if (index != 0)
- output << " n=\"" << index << '"';
-
-#if 0
- if (!node->getAttribute(SGPropertyNode::READ))
- output << " read=\"n\"";
-
- if (!node->getAttribute(SGPropertyNode::WRITE))
- output << " write=\"n\"";
-
- if (node->getAttribute(SGPropertyNode::ARCHIVE))
- output << " archive=\"y\"";
-#endif
-
-}
-
-
-/**
- * Test whether a node is archivable or has archivable descendants.
- */
-static bool
-isArchivable (const SGPropertyNode * node)
-{
- // FIXME: it's inefficient to do this all the time
- if (node->getAttribute(SGPropertyNode::ARCHIVE))
- return true;
- else {
- int nChildren = node->nChildren();
- for (int i = 0; i < nChildren; i++)
- if (isArchivable(node->getChild(i)))
- return true;
- }
- return false;
-}
-
-
-static bool
-writeNode (ostream &output, const SGPropertyNode * node,
- bool write_all, int indent)
-{
- // Don't write the node or any of
- // its descendants unless it is
- // allowed to be archived.
- if (!write_all && !isArchivable(node))
- return true; // Everything's OK, but we won't write.
-
- const string name = node->getName();
- int nChildren = node->nChildren();
-
- // If there is a literal value,
- // write it first.
- if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) {
- doIndent(output, indent);
- output << '<' << name;
- writeAtts(output, node);
- if (node->isAlias() && node->getAliasTarget() != 0) {
- output << " alias=\"" << node->getAliasTarget()->getPath()
- << "\"/>" << endl;
- } else {
- if (node->getType() != SGPropertyNode::UNSPECIFIED)
- output << " type=\"" << getTypeName(node->getType()) << '"';
- output << '>';
- writeData(output, node->getStringValue());
- output << "</" << name << '>' << endl;
- }
- }
-
- // If there are children, write them next.
- if (nChildren > 0 || node->isAlias()) {
- doIndent(output, indent);
- output << '<' << name;
- writeAtts(output, node);
- output << '>' << endl;
- for (int i = 0; i < nChildren; i++)
- writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP);
- doIndent(output, indent);
- output << "</" << name << '>' << endl;
- }
-
- return true;
-}
-
-
-void
-writeProperties (ostream &output, const SGPropertyNode * start_node,
- bool write_all)
-{
- int nChildren = start_node->nChildren();
-
- output << "<?xml version=\"1.0\"?>" << endl << endl;
- output << "<PropertyList>" << endl;
-
- for (int i = 0; i < nChildren; i++) {
- writeNode(output, start_node->getChild(i), write_all, INDENT_STEP);
- }
-
- output << "</PropertyList>" << endl;
-}
-
-
-void
-writeProperties (const string &file, const SGPropertyNode * start_node,
- bool write_all)
-{
- ofstream output(file.c_str());
- if (output.good()) {
- writeProperties(output, start_node, write_all);
- } else {
- throw sg_io_exception("Cannot open file", sg_location(file));
- }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Copy properties from one tree 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)
-{
- bool retval = true;
-
- // First, copy the actual value,
- // if any.
- if (in->hasValue()) {
- switch (in->getType()) {
- case SGPropertyNode::BOOL:
- if (!out->setBoolValue(in->getBoolValue()))
- retval = false;
- break;
- case SGPropertyNode::INT:
- if (!out->setIntValue(in->getIntValue()))
- retval = false;
- break;
- case SGPropertyNode::LONG:
- if (!out->setLongValue(in->getLongValue()))
- retval = false;
- break;
- case SGPropertyNode::FLOAT:
- if (!out->setFloatValue(in->getFloatValue()))
- retval = false;
- break;
- case SGPropertyNode::DOUBLE:
- if (!out->setDoubleValue(in->getDoubleValue()))
- retval = false;
- break;
- case SGPropertyNode::STRING:
- if (!out->setStringValue(in->getStringValue()))
- retval = false;
- break;
- case SGPropertyNode::UNSPECIFIED:
- if (!out->setUnspecifiedValue(in->getStringValue()))
- retval = false;
- break;
- default:
- string message = "Unknown internal SGPropertyNode type";
- message += in->getType();
- throw sg_error(message, "SimGear Property Reader");
- }
- }
-
- // Next, copy the children.
- int nChildren = in->nChildren();
- for (int i = 0; i < nChildren; i++) {
- const SGPropertyNode * in_child = in->getChild(i);
- SGPropertyNode * out_child = out->getChild(in_child->getName(),
- in_child->getIndex(),
- true);
- if (!copyProperties(in_child, out_child))
- retval = false;
- }
-
- return retval;
-}
-
-// end of props_io.cxx
+++ /dev/null
-/**
- * \file props_io.hxx
- * Interface definition for property list io.
- * Started Fall 2000 by David Megginson, david@megginson.com
- * This code is released into the Public Domain.
- *
- * See props.html for documentation [replace with URL when available].
- *
- * $Id$
- */
-
-#ifndef __PROPS_IO_HXX
-#define __PROPS_IO_HXX
-
-#include <simgear/compiler.h>
-#include <simgear/misc/props.hxx>
-
-#include <stdio.h>
-
-#include STL_STRING
-#include <vector>
-#include <map>
-#include STL_IOSTREAM
-
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(map);
-SG_USING_STD(istream);
-SG_USING_STD(ostream);
-
-/**
- * Read properties from an XML input stream.
- */
-void readProperties (istream &input, SGPropertyNode * start_node,
- const string &base = "");
-
-
-/**
- * Read properties from an XML file.
- */
-void readProperties (const string &file, SGPropertyNode * start_node);
-
-
-/**
- * Write properties to an XML output stream.
- */
-void writeProperties (ostream &output, const SGPropertyNode * start_node,
- bool write_all = false);
-
-
-/**
- * Write properties to an XML file.
- */
-void writeProperties (const string &file, const SGPropertyNode * start_node,
- bool write_all = false);
-
-
-/**
- * Copy properties from one node to another.
- */
-bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out);
-
-
-#endif // __PROPS_IO_HXX
-
-// end of props_io.hxx
+++ /dev/null
-
-////////////////////////////////////////////////////////////////////////
-// Test harness.
-////////////////////////////////////////////////////////////////////////
-
-#include <simgear/compiler.h>
-
-#include STL_IOSTREAM
-#include "props.hxx"
-#include "props_io.hxx"
-
-SG_USING_STD(cout);
-SG_USING_STD(cerr);
-SG_USING_STD(endl);
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Sample object.
-////////////////////////////////////////////////////////////////////////
-
-class Stuff {
-public:
- Stuff () : _stuff(199.0) {}
- virtual float getStuff () const { return _stuff; }
- virtual void setStuff (float stuff) { _stuff = stuff; }
- virtual float getStuff (int index) const { return _stuff * index; }
- virtual void setStuff (int index, float val) {
- _stuff = val / index;
- }
-private:
- float _stuff;
-};
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Sample function.
-////////////////////////////////////////////////////////////////////////
-
-static int get100 () { return 100; }
-
-static double getNum (int index) { return 1.0 / index; }
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Show a value.
-////////////////////////////////////////////////////////////////////////
-
-static void
-show_values (const SGPropertyNode * node)
-{
- cout << "Bool: " << (node->getBoolValue() ? "true" : "false") << endl;
- cout << "Int: " << node->getIntValue() << endl;
- cout << "Float: " << node->getFloatValue() << endl;
- cout << "Double: " << node->getDoubleValue() << endl;
- cout << "String: " << node->getStringValue() << endl;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Test individual values.
-////////////////////////////////////////////////////////////////////////
-
-static void
-test_value ()
-{
- SGPropertyNode * node;
-
- cout << endl << "Value" << endl << endl;
-
- //
- // Test coercion for getters.
- //
-
- cout << "Testing coercion from bool (expect true)" << endl;
- node = new SGPropertyNode;
- node->setBoolValue(true);
- show_values(node);
- delete node;
- cout << endl;
-
- cout << "Testing coercion from int (expect 128)" << endl;
- node = new SGPropertyNode;
- node->setIntValue(128);
- show_values(node);
- delete node;
- cout << endl;
-
- cout << "Testing coercion from float (expect 1.0/3.0)" << endl;
- node = new SGPropertyNode;
- node->setFloatValue(1.0/3.0);
- show_values(node);
- delete node;
- cout << endl;
-
- cout << "Testing coercion from double (expect 1.0/3.0)" << endl;
- node = new SGPropertyNode;
- node->setDoubleValue(1.0/3.0);
- show_values(node);
- delete node;
- cout << endl;
-
- cout << "Testing coercion from string (expect 10e4)" << endl;
- node = new SGPropertyNode;
- node->setStringValue("10e4");
- show_values(node);
- delete node;
- cout << endl;
-
- cout << "Testing coercion from unspecified (expect -10e-4)" << endl;
- node = new SGPropertyNode;
- node->setUnspecifiedValue("-10e-4");
- show_values(node);
- delete node;
- cout << endl;
-
- //
- // Test coercion for setters.
- //
-
- node = new SGPropertyNode;
-
- cout << "Testing coercion to bool from bool (expect false)" << endl;
- node->setBoolValue(false);
- show_values(node);
- cout << endl;
-
- cout << "Testing coercion to bool from int (expect 1)" << endl;
- node->setIntValue(1);
- show_values(node);
- cout << endl;
-
- cout << "Testing coercion to bool from float (expect 1.1)" << endl;
- node->setFloatValue(1.1);
- show_values(node);
- cout << endl;
-
- cout << "Testing coercion to bool from double (expect 1.1)" << endl;
- node->setDoubleValue(1.1);
- show_values(node);
- cout << endl;
-
- cout << "Testing coercion to bool from string (expect 1e10)" << endl;
- node->setStringValue("1e10");
- show_values(node);
- cout << endl;
-
- cout << "Testing coercion to bool from unspecified (expect 1e10)" << endl;
- node->setUnspecifiedValue("1e10");
- show_values(node);
- cout << endl;
-
- // Test tying to a pointer.
-
- static int myValue = 10;
-
- cout << "Testing tying to a pointer (expect 10)" << endl;
- if (!node->tie(SGRawValuePointer<int>(&myValue), false))
- cout << "*** FAILED TO TIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Changing base variable (expect -5)" << endl;
- myValue = -5;
- show_values(node);
- if (!node->untie())
- cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
- cout << endl;
-
-
- // Test tying to static functions.
-
- cout << "Create a new int value (expect 10)" << endl;
- node->setIntValue(10);
- show_values(node);
- cout << endl;
-
- cout << "Testing tying to static getter (expect 100)" << endl;
- if (!node->tie(SGRawValueFunctions<int>(get100)))
- cout << "*** FAILED TO TIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Try changing value with no setter (expect 100)" << endl;
- if (node->setIntValue(200))
- cout << "*** setIntValue did not return false!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Untie value (expect 100)" << endl;
- if (!node->untie())
- cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Try changing value (expect 200)" << endl;
- if (!node->setIntValue(200))
- cout << "*** setIntValue RETURNED FALSE!!!" << endl;
- show_values(node);
- cout << endl;
-
- // Test tying to indexed static functions.
-
- cout << "Create a new int value (expect 10)" << endl;
- node->setIntValue(10);
- show_values(node);
- cout << endl;
-
- cout << "Testing tying to indexed static getter (0.3333...)" << endl;
- if (!node->tie(SGRawValueFunctionsIndexed<double>(3, getNum)))
- cout << "*** FAILED TO TIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Untie value (expect 0.3333...)" << endl;
- if (!node->untie())
- cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
-
- // Test methods.
-
- cout << "Try tying to an object without defaults (expect 199)" << endl;
- Stuff stuff;
- SGRawValueMethods<class Stuff,float> tiedstuff(stuff,
- &Stuff::getStuff,
- &Stuff::setStuff);
- if (!node->tie(tiedstuff, false))
- cout << "*** FAILED TO TIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Try untying from object (expect 199)" << endl;
- if (!node->untie())
- cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Try tying to an indexed method (expect 199)" << endl;
- if (!node->tie(SGRawValueMethodsIndexed<class Stuff, float>
- (stuff, 2, &Stuff::getStuff, &Stuff::setStuff)))
- cout << "*** FAILED TO TIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- node->untie();
-
- cout << "Change value (expect 50)" << endl;
- if (!node->setIntValue(50))
- cout << "*** FAILED TO SET VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- cout << "Try tying to an object with defaults (expect 50)" << endl;
- if (!node->tie(tiedstuff, true))
- cout << "*** FAILED TO TIE VALUE!!!" << endl;
- show_values(node);
- cout << endl;
-
- delete node;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Check property nodes.
-////////////////////////////////////////////////////////////////////////
-
-static void
-dump_node (const SGPropertyNode * node)
-{
- writeProperties(cout, node, true);
-}
-
-static void
-test_property_nodes ()
-{
- SGPropertyNode root;
- cout << "Created root node " << root.getPath() << endl;
-
- SGPropertyNode * child = root.getChild("foo", 0, true);
-
- SGPropertyNode *grandchild = child->getChild("bar", 0, true);
- grandchild->setDoubleValue(100);
- grandchild = child->getChild("bar", 1, true);
- grandchild->setDoubleValue(200);
- grandchild = child->getChild("bar", 2, true);
- grandchild->setDoubleValue(300);
- grandchild = child->getChild("bar", 3, true);
- grandchild->setDoubleValue(400);
-
- child = root.getChild("hack", 0, true);
-
- grandchild = child->getChild("bar", 0, true);
- grandchild->setDoubleValue(100);
- grandchild = child->getChild("bar", 3, true);
- grandchild->setDoubleValue(200);
- grandchild = child->getChild("bar", 1, true);
- grandchild->setDoubleValue(300);
- grandchild = child->getChild("bar", 2, true);
- grandchild->setDoubleValue(400);
- dump_node(&root);
-
- cout << "Trying path (expect /foo[0]/bar[0])" << endl;
- grandchild = root.getNode("/hack/../foo/./bar[0]");
- cout << "Path is " << grandchild->getPath() << endl;
- cout << endl;
-
- cout << "Looking for all /hack[0]/bar children" << endl;
- vector<SGPropertyNode_ptr> bar = child->getChildren("bar");
- cout << "There are " << bar.size() << " matches" << endl;
- for (int i = 0; i < (int)bar.size(); i++)
- cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl;
- cout << endl;
-
- cout << "Testing addition of a totally empty node" << endl;
- if (root.getNode("/a/b/c", true) == 0)
- cerr << "** failed to create /a/b/c" << endl;
- dump_node(&root);
- cout << endl;
-}
-
-
-int main (int ac, char ** av)
-{
- test_value();
- test_property_nodes();
-
- for (int i = 1; i < ac; i++) {
- try {
- cout << "Reading " << av[i] << endl;
- SGPropertyNode root;
- readProperties(av[i], &root);
- writeProperties(cout, &root, true);
- cout << endl;
- } catch (string &message) {
- cout << "Aborted with " << message << endl;
- }
- }
-
- return 0;
-}
--- /dev/null
+includedir = @includedir@/props
+
+lib_LIBRARIES = libsgprops.a
+
+include_HEADERS = \
+ condition.hxx \
+ props.hxx \
+ props_io.hxx
+
+libsgprops_a_SOURCES = \
+ condition.cxx \
+ props.cxx \
+ props_io.cxx
+
+noinst_PROGRAMS = props_test
+
+props_test_SOURCES = props_test.cxx
+props_test_LDADD = \
+ $(top_builddir)/simgear/props/libsgprops.a \
+ $(top_builddir)/simgear/xml/libsgxml.a \
+ $(top_builddir)/simgear/misc/libsgmisc.a \
+ $(top_builddir)/simgear/debug/libsgdebug.a
+
+INCLUDES = -I$(top_srcdir)
--- /dev/null
+// condition.hxx - Declarations and inline methods for property conditions.
+// Written by David Megginson, started 2000.
+// CLO May 2003 - Split out condition specific code.
+//
+// This file is in the Public Domain, and comes with no warranty.
+//
+// $Id$
+
+#ifdef HAVE_CONFIG_H
+# include <simgear/compiler.h>
+#endif
+
+// #include STL_IOSTREAM
+
+#include <simgear/misc/exception.hxx>
+
+#include "props.hxx"
+
+#include "condition.hxx"
+
+SG_USING_STD(istream);
+SG_USING_STD(ostream);
+
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGCondition::FGCondition ()
+{
+}
+
+FGCondition::~FGCondition ()
+{
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGPropertyCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGPropertyCondition::FGPropertyCondition ( SGPropertyNode *prop_root,
+ const char *propname )
+ : _node( prop_root->getNode(propname, true) )
+{
+ cout << "FGPropertyCondition::FGPropertyCondition()" << endl;
+ cout << " prop_root = " << prop_root << endl;
+ cout << " propname = " << propname << endl;
+ _node = prop_root->getNode(propname, true);
+ cout << " _node = " << _node << endl;
+}
+
+FGPropertyCondition::~FGPropertyCondition ()
+{
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGNotCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGNotCondition::FGNotCondition (FGCondition * condition)
+ : _condition(condition)
+{
+}
+
+FGNotCondition::~FGNotCondition ()
+{
+ delete _condition;
+}
+
+bool
+FGNotCondition::test () const
+{
+ return !(_condition->test());
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGAndCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGAndCondition::FGAndCondition ()
+{
+}
+
+FGAndCondition::~FGAndCondition ()
+{
+ for (unsigned int i = 0; i < _conditions.size(); i++)
+ delete _conditions[i];
+}
+
+bool
+FGAndCondition::test () const
+{
+ int nConditions = _conditions.size();
+ for (int i = 0; i < nConditions; i++) {
+ if (!_conditions[i]->test())
+ return false;
+ }
+ return true;
+}
+
+void
+FGAndCondition::addCondition (FGCondition * condition)
+{
+ _conditions.push_back(condition);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGOrCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGOrCondition::FGOrCondition ()
+{
+}
+
+FGOrCondition::~FGOrCondition ()
+{
+ for (unsigned int i = 0; i < _conditions.size(); i++)
+ delete _conditions[i];
+}
+
+bool
+FGOrCondition::test () const
+{
+ int nConditions = _conditions.size();
+ for (int i = 0; i < nConditions; i++) {
+ if (_conditions[i]->test())
+ return true;
+ }
+ return false;
+}
+
+void
+FGOrCondition::addCondition (FGCondition * condition)
+{
+ _conditions.push_back(condition);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGComparisonCondition.
+////////////////////////////////////////////////////////////////////////
+
+static int
+doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
+{
+ switch (left->getType()) {
+ case SGPropertyNode::BOOL: {
+ bool v1 = left->getBoolValue();
+ bool v2 = right->getBoolValue();
+ if (v1 < v2)
+ return FGComparisonCondition::LESS_THAN;
+ else if (v1 > v2)
+ return FGComparisonCondition::GREATER_THAN;
+ else
+ return FGComparisonCondition::EQUALS;
+ break;
+ }
+ case SGPropertyNode::INT: {
+ int v1 = left->getIntValue();
+ int v2 = right->getIntValue();
+ if (v1 < v2)
+ return FGComparisonCondition::LESS_THAN;
+ else if (v1 > v2)
+ return FGComparisonCondition::GREATER_THAN;
+ else
+ return FGComparisonCondition::EQUALS;
+ break;
+ }
+ case SGPropertyNode::LONG: {
+ long v1 = left->getLongValue();
+ long v2 = right->getLongValue();
+ if (v1 < v2)
+ return FGComparisonCondition::LESS_THAN;
+ else if (v1 > v2)
+ return FGComparisonCondition::GREATER_THAN;
+ else
+ return FGComparisonCondition::EQUALS;
+ break;
+ }
+ case SGPropertyNode::FLOAT: {
+ float v1 = left->getFloatValue();
+ float v2 = right->getFloatValue();
+ if (v1 < v2)
+ return FGComparisonCondition::LESS_THAN;
+ else if (v1 > v2)
+ return FGComparisonCondition::GREATER_THAN;
+ else
+ return FGComparisonCondition::EQUALS;
+ break;
+ }
+ case SGPropertyNode::DOUBLE: {
+ double v1 = left->getDoubleValue();
+ double v2 = right->getDoubleValue();
+ if (v1 < v2)
+ return FGComparisonCondition::LESS_THAN;
+ else if (v1 > v2)
+ return FGComparisonCondition::GREATER_THAN;
+ else
+ return FGComparisonCondition::EQUALS;
+ break;
+ }
+ case SGPropertyNode::STRING:
+ case SGPropertyNode::NONE:
+ case SGPropertyNode::UNSPECIFIED: {
+ string v1 = left->getStringValue();
+ string v2 = right->getStringValue();
+ if (v1 < v2)
+ return FGComparisonCondition::LESS_THAN;
+ else if (v1 > v2)
+ return FGComparisonCondition::GREATER_THAN;
+ else
+ return FGComparisonCondition::EQUALS;
+ break;
+ }
+ }
+ throw sg_exception("Unrecognized node type");
+ return 0;
+}
+
+
+FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
+ : _type(type),
+ _reverse(reverse),
+ _left_property(0),
+ _right_property(0),
+ _right_value(0)
+{
+}
+
+FGComparisonCondition::~FGComparisonCondition ()
+{
+ delete _right_value;
+}
+
+bool
+FGComparisonCondition::test () const
+{
+ // Always fail if incompletely specified
+ if (_left_property == 0 ||
+ (_right_property == 0 && _right_value == 0))
+ return false;
+
+ // Get LESS_THAN, EQUALS, or GREATER_THAN
+ int cmp =
+ doComparison(_left_property,
+ (_right_property != 0 ? _right_property : _right_value));
+ if (!_reverse)
+ return (cmp == _type);
+ else
+ return (cmp != _type);
+}
+
+void
+FGComparisonCondition::setLeftProperty( SGPropertyNode *prop_root,
+ const char * propname )
+{
+ _left_property = prop_root->getNode(propname, true);
+}
+
+void
+FGComparisonCondition::setRightProperty( SGPropertyNode *prop_root,
+ const char * propname )
+{
+ delete _right_value;
+ _right_value = 0;
+ _right_property = prop_root->getNode(propname, true);
+}
+
+void
+FGComparisonCondition::setRightValue (const SGPropertyNode *node)
+{
+ _right_property = 0;
+ delete _right_value;
+ _right_value = new SGPropertyNode(*node);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Read a condition and use it if necessary.
+////////////////////////////////////////////////////////////////////////
+
+ // Forward declaration
+static FGCondition * readCondition( SGPropertyNode *prop_root,
+ const SGPropertyNode *node );
+
+static FGCondition *
+readPropertyCondition( SGPropertyNode *prop_root,
+ const SGPropertyNode *node )
+{
+ return new FGPropertyCondition( prop_root, node->getStringValue() );
+}
+
+static FGCondition *
+readNotCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+ int nChildren = node->nChildren();
+ for (int i = 0; i < nChildren; i++) {
+ const SGPropertyNode * child = node->getChild(i);
+ FGCondition * condition = readCondition(prop_root, child);
+ if (condition != 0)
+ return new FGNotCondition(condition);
+ }
+ SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
+ return 0;
+}
+
+static FGCondition *
+readAndConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+ FGAndCondition * andCondition = new FGAndCondition;
+ int nChildren = node->nChildren();
+ for (int i = 0; i < nChildren; i++) {
+ const SGPropertyNode * child = node->getChild(i);
+ FGCondition * condition = readCondition(prop_root, child);
+ if (condition != 0)
+ andCondition->addCondition(condition);
+ }
+ return andCondition;
+}
+
+static FGCondition *
+readOrConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+ FGOrCondition * orCondition = new FGOrCondition;
+ int nChildren = node->nChildren();
+ for (int i = 0; i < nChildren; i++) {
+ const SGPropertyNode * child = node->getChild(i);
+ FGCondition * condition = readCondition(prop_root, child);
+ if (condition != 0)
+ orCondition->addCondition(condition);
+ }
+ return orCondition;
+}
+
+static FGCondition *
+readComparison( SGPropertyNode *prop_root,
+ const SGPropertyNode *node,
+ FGComparisonCondition::Type type,
+ bool reverse)
+{
+ FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
+ condition->setLeftProperty(prop_root, node->getStringValue("property[0]"));
+ if (node->hasValue("property[1]"))
+ condition->setRightProperty(prop_root, node->getStringValue("property[1]"));
+ else
+ condition->setRightValue(node->getChild("value", 0));
+
+ return condition;
+}
+
+static FGCondition *
+readCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+ const string &name = node->getName();
+ if (name == "property")
+ return readPropertyCondition(prop_root, node);
+ else if (name == "not")
+ return readNotCondition(prop_root, node);
+ else if (name == "and")
+ return readAndConditions(prop_root, node);
+ else if (name == "or")
+ return readOrConditions(prop_root, node);
+ else if (name == "less-than")
+ return readComparison(prop_root, node, FGComparisonCondition::LESS_THAN,
+ false);
+ else if (name == "less-than-equals")
+ return readComparison(prop_root, node, FGComparisonCondition::GREATER_THAN,
+ true);
+ else if (name == "greater-than")
+ return readComparison(prop_root, node, FGComparisonCondition::GREATER_THAN,
+ false);
+ else if (name == "greater-than-equals")
+ return readComparison(prop_root, node, FGComparisonCondition::LESS_THAN,
+ true);
+ else if (name == "equals")
+ return readComparison(prop_root, node, FGComparisonCondition::EQUALS,
+ false);
+ else if (name == "not-equals")
+ return readComparison(prop_root, node, FGComparisonCondition::EQUALS, true);
+ else
+ return 0;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGConditional.
+////////////////////////////////////////////////////////////////////////
+
+FGConditional::FGConditional ()
+ : _condition (0)
+{
+}
+
+FGConditional::~FGConditional ()
+{
+ delete _condition;
+}
+
+void
+FGConditional::setCondition (FGCondition * condition)
+{
+ delete _condition;
+ _condition = condition;
+}
+
+bool
+FGConditional::test () const
+{
+ return ((_condition == 0) || _condition->test());
+}
+
+
+\f
+// The top-level is always an implicit 'and' group
+FGCondition *
+fgReadCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+ return readAndConditions(prop_root, node);
+}
+
+
+// end of fg_props.cxx
--- /dev/null
+// condition.hxx - Declarations and inline methods for property conditions.
+// Written by David Megginson, started 2000.
+// CLO May 2003 - Split out condition specific code.
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#ifndef __SG_CONDITION_HXX
+#define __SG_CONDITION_HXX
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx>
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Conditions.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * An encoded condition.
+ *
+ * This class encodes a single condition of some sort, possibly
+ * connected with properties.
+ *
+ * This class should migrate to somewhere more general.
+ */
+class FGCondition
+{
+public:
+ FGCondition ();
+ virtual ~FGCondition ();
+ virtual bool test () const = 0;
+};
+
+
+/**
+ * Condition for a single property.
+ *
+ * This condition is true only if the property returns a boolean
+ * true value.
+ */
+class FGPropertyCondition : public FGCondition
+{
+public:
+ FGPropertyCondition ( SGPropertyNode *prop_root,
+ const char * propname );
+ virtual ~FGPropertyCondition ();
+ virtual bool test () const { return _node->getBoolValue(); }
+private:
+ const SGPropertyNode * _node;
+};
+
+
+/**
+ * Condition for a 'not' operator.
+ *
+ * This condition is true only if the child condition is false.
+ */
+class FGNotCondition : public FGCondition
+{
+public:
+ // transfer pointer ownership
+ FGNotCondition (FGCondition * condition);
+ virtual ~FGNotCondition ();
+ virtual bool test () const;
+private:
+ FGCondition * _condition;
+};
+
+
+/**
+ * Condition for an 'and' group.
+ *
+ * This condition is true only if all of the conditions
+ * in the group are true.
+ */
+class FGAndCondition : public FGCondition
+{
+public:
+ FGAndCondition ();
+ virtual ~FGAndCondition ();
+ virtual bool test () const;
+ // transfer pointer ownership
+ virtual void addCondition (FGCondition * condition);
+private:
+ vector<FGCondition *> _conditions;
+};
+
+
+/**
+ * Condition for an 'or' group.
+ *
+ * This condition is true if at least one of the conditions in the
+ * group is true.
+ */
+class FGOrCondition : public FGCondition
+{
+public:
+ FGOrCondition ();
+ virtual ~FGOrCondition ();
+ virtual bool test () const;
+ // transfer pointer ownership
+ virtual void addCondition (FGCondition * condition);
+private:
+ vector<FGCondition *> _conditions;
+};
+
+
+/**
+ * Abstract base class for property comparison conditions.
+ */
+class FGComparisonCondition : public FGCondition
+{
+public:
+ enum Type {
+ LESS_THAN,
+ GREATER_THAN,
+ EQUALS
+ };
+ FGComparisonCondition (Type type, bool reverse = false);
+ virtual ~FGComparisonCondition ();
+ virtual bool test () const;
+ virtual void setLeftProperty( SGPropertyNode *prop_root,
+ const char * propname );
+ virtual void setRightProperty( SGPropertyNode *prop_root,
+ const char * propname );
+ // will make a local copy
+ virtual void setRightValue (const SGPropertyNode * value);
+private:
+ Type _type;
+ bool _reverse;
+ const SGPropertyNode * _left_property;
+ const SGPropertyNode * _right_property;
+ const SGPropertyNode * _right_value;
+};
+
+
+/**
+ * Base class for a conditional components.
+ *
+ * This class manages the conditions and tests; the component should
+ * invoke the test() method whenever it needs to decide whether to
+ * active itself, draw itself, and so on.
+ */
+class FGConditional
+{
+public:
+ FGConditional ();
+ virtual ~FGConditional ();
+ // transfer pointer ownership
+ virtual void setCondition (FGCondition * condition);
+ virtual const FGCondition * getCondition () const { return _condition; }
+ virtual bool test () const;
+private:
+ FGCondition * _condition;
+};
+
+
+/**
+ * Global function to make a condition out of properties.
+ *
+ * The top-level is always an implicit 'and' group, whatever the
+ * node's name (it should usually be "condition").
+ *
+ * @param node The top-level condition node (usually named "condition").
+ * @return A pointer to a newly-allocated condition; it is the
+ * responsibility of the caller to delete the condition when
+ * it is no longer needed.
+ */
+FGCondition * fgReadCondition( SGPropertyNode *prop_root,
+ const SGPropertyNode *node );
+
+
+#endif // __SG_CONDITION_HXX
+
--- /dev/null
+// props.cxx - implementation of a property list.
+// Started Fall 2000 by David Megginson, david@megginson.com
+// This code is released into the Public Domain.
+//
+// See props.html for documentation [replace with URL when available].
+//
+// $Id$
+
+#include "props.hxx"
+
+#include <algorithm>
+#include <stdio.h>
+#include <string.h>
+
+#if PROPS_STANDALONE
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+using std::find;
+using std::sort;
+using std::vector;
+
+#else
+
+#include <simgear/compiler.h>
+#include <simgear/debug/logstream.hxx>
+
+SG_USING_STD(sort);
+SG_USING_STD(find);
+SG_USING_STD(vector);
+
+#ifdef _MSC_VER
+// MSVC is buggy, and needs something strange here
+SG_USING_STD(vector<SGPropertyNode_ptr>);
+SG_USING_STD(vector<SGPropertyChangeListener *>);
+SG_USING_STD(vector<SGPropertyNode *>);
+#endif
+
+#endif
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Local classes.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Comparator class for sorting by index.
+ */
+class CompareIndices
+{
+public:
+ int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
+ return (n1->getIndex() < n2->getIndex());
+ }
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Convenience macros for value access.
+////////////////////////////////////////////////////////////////////////
+
+#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
+#define TEST_WRITE if (!getAttribute(WRITE)) return false
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Default values for every type.
+////////////////////////////////////////////////////////////////////////
+
+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 char * const SGRawValue<const char *>::DefaultValue = "";
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Local path normalization code.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * A component in a path.
+ */
+struct PathComponent
+{
+ string name;
+ int index;
+};
+
+/**
+ * Parse the name for a path component.
+ *
+ * Name: [_a-zA-Z][-._a-zA-Z0-9]*
+ */
+static inline const string
+parse_name (const string &path, int &i)
+{
+ string name = "";
+ int max = path.size();
+
+ if (path[i] == '.') {
+ i++;
+ if (i < max && path[i] == '.') {
+ i++;
+ name = "..";
+ } else {
+ name = ".";
+ }
+ if (i < max && path[i] != '/')
+ throw string(string("Illegal character after ") + name);
+ }
+
+ else if (isalpha(path[i]) || path[i] == '_') {
+ name += path[i];
+ i++;
+
+ // The rules inside a name are a little
+ // less restrictive.
+ while (i < max) {
+ if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
+ path[i] == '-' || path[i] == '.') {
+ name += path[i];
+ } else if (path[i] == '[' || path[i] == '/') {
+ break;
+ } else {
+ throw string("name may contain only ._- and alphanumeric characters");
+ }
+ i++;
+ }
+ }
+
+ else {
+ if (name.size() == 0)
+ throw string("name must begin with alpha or '_'");
+ }
+
+ return name;
+}
+
+
+/**
+ * Parse the optional integer index for a path component.
+ *
+ * Index: "[" [0-9]+ "]"
+ */
+static inline int
+parse_index (const string &path, int &i)
+{
+ int index = 0;
+
+ if (path[i] != '[')
+ return 0;
+ else
+ i++;
+
+ for (int max = path.size(); i < max; i++) {
+ if (isdigit(path[i])) {
+ index = (index * 10) + (path[i] - '0');
+ } else if (path[i] == ']') {
+ i++;
+ return index;
+ } else {
+ break;
+ }
+ }
+
+ throw string("unterminated index (looking for ']')");
+}
+
+
+/**
+ * Parse a single path component.
+ *
+ * Component: Name Index?
+ */
+static inline PathComponent
+parse_component (const string &path, int &i)
+{
+ PathComponent component;
+ component.name = parse_name(path, i);
+ if (component.name[0] != '.')
+ component.index = parse_index(path, i);
+ else
+ component.index = -1;
+ return component;
+}
+
+
+/**
+ * Parse a path into its components.
+ */
+static void
+parse_path (const string &path, vector<PathComponent> &components)
+{
+ int pos = 0;
+ int max = path.size();
+
+ // Check for initial '/'
+ if (path[pos] == '/') {
+ PathComponent root;
+ root.name = "";
+ root.index = -1;
+ components.push_back(root);
+ pos++;
+ while (pos < max && path[pos] == '/')
+ pos++;
+ }
+
+ while (pos < max) {
+ components.push_back(parse_component(path, pos));
+ while (pos < max && path[pos] == '/')
+ pos++;
+ }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Other static utility functions.
+////////////////////////////////////////////////////////////////////////
+
+
+static char *
+copy_string (const char * s)
+{
+ // FIXME: potential buffer overflow.
+ // For some reason, strnlen and
+ // strncpy cause all kinds of crashes.
+ char * copy = new char[strlen(s) + 1];
+ strcpy(copy, s);
+ return copy;
+}
+
+static bool
+compare_strings (const char * s1, const char * s2)
+{
+ return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
+}
+
+/**
+ * Locate a child node by name and index.
+ */
+static int
+find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
+{
+ int nNodes = nodes.size();
+ for (int i = 0; i < nNodes; i++) {
+ SGPropertyNode * node = nodes[i];
+ if (compare_strings(node->getName(), name) && node->getIndex() == index)
+ return i;
+ }
+ return -1;
+}
+
+
+/**
+ * Locate another node, given a relative path.
+ */
+static SGPropertyNode *
+find_node (SGPropertyNode * current,
+ const vector<PathComponent> &components,
+ 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 >= (int)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)
+ throw string("Attempt to move past root with '..'");
+ else
+ return find_node(parent, components, position + 1, create);
+ }
+
+ // Otherwise, a child name
+ else {
+ SGPropertyNode * child =
+ current->getChild(components[position].name.c_str(),
+ components[position].index,
+ create);
+ return find_node(child, components, position + 1, create);
+ }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Private methods from SGPropertyNode (may be inlined for speed).
+////////////////////////////////////////////////////////////////////////
+
+inline bool
+SGPropertyNode::get_bool () const
+{
+ if (_tied)
+ return _value.bool_val->getValue();
+ else
+ return _local_val.bool_val;
+}
+
+inline int
+SGPropertyNode::get_int () const
+{
+ if (_tied)
+ return _value.int_val->getValue();
+ else
+ return _local_val.int_val;
+}
+
+inline long
+SGPropertyNode::get_long () const
+{
+ if (_tied)
+ return _value.long_val->getValue();
+ else
+ return _local_val.long_val;
+}
+
+inline float
+SGPropertyNode::get_float () const
+{
+ if (_tied)
+ return _value.float_val->getValue();
+ else
+ return _local_val.float_val;
+}
+
+inline double
+SGPropertyNode::get_double () const
+{
+ if (_tied)
+ return _value.double_val->getValue();
+ else
+ return _local_val.double_val;
+}
+
+inline const char *
+SGPropertyNode::get_string () const
+{
+ if (_tied)
+ return _value.string_val->getValue();
+ else
+ return _local_val.string_val;
+}
+
+inline bool
+SGPropertyNode::set_bool (bool val)
+{
+ if (_tied) {
+ if (_value.bool_val->setValue(val)) {
+ fireValueChanged();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _local_val.bool_val = val;
+ fireValueChanged();
+ return true;
+ }
+}
+
+inline bool
+SGPropertyNode::set_int (int val)
+{
+ if (_tied) {
+ if (_value.int_val->setValue(val)) {
+ fireValueChanged();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _local_val.int_val = val;
+ fireValueChanged();
+ return true;
+ }
+}
+
+inline bool
+SGPropertyNode::set_long (long val)
+{
+ if (_tied) {
+ if (_value.long_val->setValue(val)) {
+ fireValueChanged();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _local_val.long_val = val;
+ fireValueChanged();
+ return true;
+ }
+}
+
+inline bool
+SGPropertyNode::set_float (float val)
+{
+ if (_tied) {
+ if (_value.float_val->setValue(val)) {
+ fireValueChanged();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _local_val.float_val = val;
+ fireValueChanged();
+ return true;
+ }
+}
+
+inline bool
+SGPropertyNode::set_double (double val)
+{
+ if (_tied) {
+ if (_value.double_val->setValue(val)) {
+ fireValueChanged();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _local_val.double_val = val;
+ fireValueChanged();
+ return true;
+ }
+}
+
+inline bool
+SGPropertyNode::set_string (const char * val)
+{
+ if (_tied) {
+ if (_value.string_val->setValue(val)) {
+ fireValueChanged();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ delete [] _local_val.string_val;
+ _local_val.string_val = copy_string(val);
+ fireValueChanged();
+ return true;
+ }
+}
+
+void
+SGPropertyNode::clear_value ()
+{
+ switch (_type) {
+ case NONE:
+ break;
+ case ALIAS:
+ _value.alias = 0;
+ break;
+ case BOOL:
+ if (_tied) {
+ delete _value.bool_val;
+ _value.bool_val = 0;
+ }
+ _local_val.bool_val = SGRawValue<bool>::DefaultValue;
+ break;
+ case INT:
+ if (_tied) {
+ delete _value.int_val;
+ _value.int_val = 0;
+ }
+ _local_val.int_val = SGRawValue<int>::DefaultValue;
+ break;
+ case LONG:
+ if (_tied) {
+ delete _value.long_val;
+ _value.long_val = 0L;
+ }
+ _local_val.long_val = SGRawValue<long>::DefaultValue;
+ break;
+ case FLOAT:
+ if (_tied) {
+ delete _value.float_val;
+ _value.float_val = 0;
+ }
+ _local_val.float_val = SGRawValue<float>::DefaultValue;
+ break;
+ case DOUBLE:
+ if (_tied) {
+ delete _value.double_val;
+ _value.double_val = 0;
+ }
+ _local_val.double_val = SGRawValue<double>::DefaultValue;
+ break;
+ case STRING:
+ case UNSPECIFIED:
+ if (_tied) {
+ delete _value.string_val;
+ _value.string_val = 0;
+ } else {
+ delete [] _local_val.string_val;
+ }
+ _local_val.string_val = 0;
+ break;
+ }
+ _tied = false;
+ _type = NONE;
+}
+
+
+/**
+ * Get the value as a string.
+ */
+const char *
+SGPropertyNode::make_string () const
+{
+ if (!getAttribute(READ))
+ return "";
+
+ switch (_type) {
+ case ALIAS:
+ return _value.alias->getStringValue();
+ case BOOL:
+ if (get_bool())
+ return "true";
+ else
+ return "false";
+ case INT:
+ sprintf(_buffer, "%d", get_int());
+ return _buffer;
+ case LONG:
+ sprintf(_buffer, "%ld", get_long());
+ return _buffer;
+ case FLOAT:
+ sprintf(_buffer, "%f", get_float());
+ return _buffer;
+ case DOUBLE:
+ sprintf(_buffer, "%f", get_double());
+ return _buffer;
+ case STRING:
+ case UNSPECIFIED:
+ return get_string();
+ case NONE:
+ default:
+ return "";
+ }
+}
+
+/**
+ * Trace a write access for a property.
+ */
+void
+SGPropertyNode::trace_write () const
+{
+#if PROPS_STANDALONE
+ cerr << "TRACE: Write node " << getPath () << ", value\""
+ << make_string() << '"' << endl;
+#else
+ SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
+ << ", value\"" << make_string() << '"');
+#endif
+}
+
+/**
+ * Trace a read access for a property.
+ */
+void
+SGPropertyNode::trace_read () const
+{
+#if PROPS_STANDALONE
+ cerr << "TRACE: Write node " << getPath () << ", value \""
+ << make_string() << '"' << endl;
+#else
+ SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath()
+ << ", value \"" << make_string() << '"');
+#endif
+}
+
+/**
+ * Increment reference counter
+ */
+void
+SGPropertyNode::incrementRef()
+{
+ ++_count;
+}
+
+/**
+ * Decrement reference counter
+ */
+int
+SGPropertyNode::decrementRef()
+{
+ return --_count;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Public methods from SGPropertyNode.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Last used attribute
+ * Update as needed when enum Attribute is changed
+ */
+const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE;
+
+/**
+ * Default constructor: always creates a root node.
+ */
+SGPropertyNode::SGPropertyNode ()
+ : _name(copy_string("")),
+ _display_name(0),
+ _index(0),
+ _parent(0),
+ _path(0),
+ _path_cache(0),
+ _type(NONE),
+ _tied(false),
+ _attr(READ|WRITE),
+ _count(0),
+ _listeners(0)
+{
+ _local_val.string_val = 0;
+}
+
+
+/**
+ * Copy constructor.
+ */
+SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
+ : _display_name(0),
+ _index(node._index),
+ _parent(0), // don't copy the parent
+ _path(0),
+ _path_cache(0),
+ _type(node._type),
+ _tied(node._tied),
+ _attr(node._attr),
+ _count(0),
+ _listeners(0) // CHECK!!
+{
+ _name = copy_string(node._name);
+ _local_val.string_val = 0;
+ switch (_type) {
+ case NONE:
+ break;
+ case ALIAS:
+ _value.alias = node._value.alias;
+ _tied = false;
+ break;
+ case BOOL:
+ if (_tied) {
+ _tied = true;
+ _value.bool_val = node._value.bool_val->clone();
+ } else {
+ _tied = false;
+ set_bool(node.get_bool());
+ }
+ break;
+ case INT:
+ if (_tied) {
+ _tied = true;
+ _value.int_val = node._value.int_val->clone();
+ } else {
+ _tied = false;
+ set_int(node.get_int());
+ }
+ break;
+ case LONG:
+ if (_tied) {
+ _tied = true;
+ _value.long_val = node._value.long_val->clone();
+ } else {
+ _tied = false;
+ set_long(node.get_long());
+ }
+ break;
+ case FLOAT:
+ if (_tied) {
+ _tied = true;
+ _value.float_val = node._value.float_val->clone();
+ } else {
+ _tied = false;
+ set_float(node.get_float());
+ }
+ break;
+ case DOUBLE:
+ if (_tied) {
+ _tied = true;
+ _value.double_val = node._value.double_val->clone();
+ } else {
+ _tied = false;
+ set_double(node.get_double());
+ }
+ break;
+ case STRING:
+ case UNSPECIFIED:
+ if (_tied) {
+ _tied = true;
+ _value.string_val = node._value.string_val->clone();
+ } else {
+ _tied = false;
+ set_string(node.get_string());
+ }
+ break;
+ }
+}
+
+
+/**
+ * Convenience constructor.
+ */
+SGPropertyNode::SGPropertyNode (const char * name,
+ int index,
+ SGPropertyNode * parent)
+ : _display_name(0),
+ _index(index),
+ _parent(parent),
+ _path(0),
+ _path_cache(0),
+ _type(NONE),
+ _tied(false),
+ _attr(READ|WRITE),
+ _count(0),
+ _listeners(0)
+{
+ _name = copy_string(name);
+ _local_val.string_val = 0;
+}
+
+
+/**
+ * Destructor.
+ */
+SGPropertyNode::~SGPropertyNode ()
+{
+ delete [] _name;
+ delete [] _display_name;
+ delete [] _path;
+ delete _path_cache;
+ clear_value();
+ delete _listeners;
+}
+
+
+/**
+ * Alias to another node.
+ */
+bool
+SGPropertyNode::alias (SGPropertyNode * target)
+{
+ if (target == 0 || _type == ALIAS || _tied)
+ return false;
+ clear_value();
+ _value.alias = target;
+ _type = ALIAS;
+ return true;
+}
+
+
+/**
+ * Alias to another node by path.
+ */
+bool
+SGPropertyNode::alias (const char * path)
+{
+ return alias(getNode(path, true));
+}
+
+
+/**
+ * Remove an alias.
+ */
+bool
+SGPropertyNode::unalias ()
+{
+ if (_type != ALIAS)
+ return false;
+ _type = NONE;
+ _value.alias = 0;
+ return true;
+}
+
+
+/**
+ * Get the target of an alias.
+ */
+SGPropertyNode *
+SGPropertyNode::getAliasTarget ()
+{
+ return (_type == ALIAS ? _value.alias : 0);
+}
+
+
+const SGPropertyNode *
+SGPropertyNode::getAliasTarget () const
+{
+ return (_type == ALIAS ? _value.alias : 0);
+}
+
+
+/**
+ * Get a non-const child by index.
+ */
+SGPropertyNode *
+SGPropertyNode::getChild (int position)
+{
+ if (position >= 0 && position < nChildren())
+ return _children[position];
+ else
+ return 0;
+}
+
+
+/**
+ * Get a const child by index.
+ */
+const SGPropertyNode *
+SGPropertyNode::getChild (int position) const
+{
+ if (position >= 0 && position < nChildren())
+ return _children[position];
+ else
+ return 0;
+}
+
+
+/**
+ * Get a non-const child by name and index, creating if necessary.
+ */
+SGPropertyNode *
+SGPropertyNode::getChild (const char * name, int index, bool create)
+{
+ int pos = find_child(name, index, _children);
+ if (pos >= 0) {
+ return _children[pos];
+ } else if (create) {
+ SGPropertyNode_ptr node;
+ pos = find_child(name, index, _removedChildren);
+ if (pos >= 0) {
+ vector<SGPropertyNode_ptr>::iterator it = _removedChildren.begin();
+ it += pos;
+ node = _removedChildren[pos];
+ _removedChildren.erase(it);
+ node->setAttribute(REMOVED, false);
+ } else {
+ node = new SGPropertyNode(name, index, this);
+ }
+ _children.push_back(node);
+ fireChildAdded(node);
+ return node;
+ } else {
+ return 0;
+ }
+}
+
+
+/**
+ * Get a const child by name and index.
+ */
+const SGPropertyNode *
+SGPropertyNode::getChild (const char * name, int index) const
+{
+ int pos = find_child(name, index, _children);
+ if (pos >= 0)
+ return _children[pos];
+ else
+ return 0;
+}
+
+
+/**
+ * Get all children with the same name (but different indices).
+ */
+vector<SGPropertyNode_ptr>
+SGPropertyNode::getChildren (const char * name) const
+{
+ vector<SGPropertyNode_ptr> children;
+ int max = _children.size();
+
+ for (int i = 0; i < max; i++)
+ if (compare_strings(_children[i]->getName(), name))
+ children.push_back(_children[i]);
+
+ sort(children.begin(), children.end(), CompareIndices());
+ return children;
+}
+
+
+/**
+ * Remove a child node
+ */
+SGPropertyNode_ptr
+SGPropertyNode::removeChild (const char * name, int index, bool keep)
+{
+ SGPropertyNode_ptr ret;
+ int pos = find_child(name, index, _children);
+ if (pos >= 0) {
+ vector<SGPropertyNode_ptr>::iterator it = _children.begin();
+ it += pos;
+ SGPropertyNode_ptr node = _children[pos];
+ _children.erase(it);
+ if (keep) {
+ _removedChildren.push_back(node);
+ }
+ node->setAttribute(REMOVED, true);
+ ret = node;
+ fireChildRemoved(node);
+ }
+ return ret;
+}
+
+
+const char *
+SGPropertyNode::getDisplayName (bool simplify) const
+{
+ string display = _name;
+ if (_index != 0 || !simplify) {
+ char buffer[64];
+ sprintf(buffer, "[%d]", _index);
+ display += buffer;
+ }
+ _display_name = copy_string(display.c_str());
+ return _display_name;
+}
+
+
+const char *
+SGPropertyNode::getPath (bool simplify) const
+{
+ // Calculate the complete path only once.
+ if (_path == 0) {
+ string path;
+ if (_parent == 0) {
+ path = "";
+ } else {
+ path = _parent->getPath(simplify);
+ path += '/';
+ path += getDisplayName(simplify);
+ }
+ _path = copy_string(path.c_str());
+ }
+
+ return _path;
+}
+
+SGPropertyNode::Type
+SGPropertyNode::getType () const
+{
+ if (_type == ALIAS)
+ return _value.alias->getType();
+ else
+ return _type;
+}
+
+
+bool
+SGPropertyNode::getBoolValue () const
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == BOOL)
+ return get_bool();
+
+ if (getAttribute(TRACE_READ))
+ trace_read();
+ if (!getAttribute(READ))
+ return SGRawValue<bool>::DefaultValue;
+ 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:
+ return get_double() == 0.0L ? false : true;
+ case STRING:
+ case UNSPECIFIED:
+ return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
+ case NONE:
+ default:
+ return SGRawValue<bool>::DefaultValue;
+ }
+}
+
+int
+SGPropertyNode::getIntValue () const
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == INT)
+ return get_int();
+
+ if (getAttribute(TRACE_READ))
+ trace_read();
+ if (!getAttribute(READ))
+ return SGRawValue<int>::DefaultValue;
+ switch (_type) {
+ case ALIAS:
+ return _value.alias->getIntValue();
+ case BOOL:
+ return int(get_bool());
+ case INT:
+ return get_int();
+ case LONG:
+ return int(get_long());
+ case FLOAT:
+ return int(get_float());
+ case DOUBLE:
+ return int(get_double());
+ case STRING:
+ case UNSPECIFIED:
+ return atoi(get_string());
+ case NONE:
+ default:
+ return SGRawValue<int>::DefaultValue;
+ }
+}
+
+long
+SGPropertyNode::getLongValue () const
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == LONG)
+ return get_long();
+
+ if (getAttribute(TRACE_READ))
+ trace_read();
+ if (!getAttribute(READ))
+ return SGRawValue<long>::DefaultValue;
+ 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 UNSPECIFIED:
+ return strtol(get_string(), 0, 0);
+ case NONE:
+ default:
+ return SGRawValue<long>::DefaultValue;
+ }
+}
+
+float
+SGPropertyNode::getFloatValue () const
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == FLOAT)
+ return get_float();
+
+ if (getAttribute(TRACE_READ))
+ trace_read();
+ if (!getAttribute(READ))
+ return SGRawValue<float>::DefaultValue;
+ switch (_type) {
+ case ALIAS:
+ return _value.alias->getFloatValue();
+ case BOOL:
+ return float(get_bool());
+ case INT:
+ return float(get_int());
+ case LONG:
+ return float(get_long());
+ case FLOAT:
+ return get_float();
+ case DOUBLE:
+ return float(get_double());
+ case STRING:
+ case UNSPECIFIED:
+ return atof(get_string());
+ case NONE:
+ default:
+ return SGRawValue<float>::DefaultValue;
+ }
+}
+
+double
+SGPropertyNode::getDoubleValue () const
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == DOUBLE)
+ return get_double();
+
+ if (getAttribute(TRACE_READ))
+ trace_read();
+ if (!getAttribute(READ))
+ return SGRawValue<double>::DefaultValue;
+
+ switch (_type) {
+ case ALIAS:
+ return _value.alias->getDoubleValue();
+ case BOOL:
+ return double(get_bool());
+ case INT:
+ return double(get_int());
+ case LONG:
+ return double(get_long());
+ case FLOAT:
+ return double(get_float());
+ case DOUBLE:
+ return get_double();
+ case STRING:
+ case UNSPECIFIED:
+ return strtod(get_string(), 0);
+ case NONE:
+ default:
+ return SGRawValue<double>::DefaultValue;
+ }
+}
+
+const char *
+SGPropertyNode::getStringValue () const
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == STRING)
+ return get_string();
+
+ if (getAttribute(TRACE_READ))
+ trace_read();
+ if (!getAttribute(READ))
+ return SGRawValue<const char *>::DefaultValue;
+ return make_string();
+}
+
+bool
+SGPropertyNode::setBoolValue (bool value)
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == BOOL)
+ return set_bool(value);
+
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE || _type == UNSPECIFIED) {
+ clear_value();
+ _tied = false;
+ _type = BOOL;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setBoolValue(value);
+ break;
+ case BOOL:
+ result = set_bool(value);
+ break;
+ case INT:
+ result = set_int(int(value));
+ break;
+ case LONG:
+ result = set_long(long(value));
+ break;
+ case FLOAT:
+ result = set_float(float(value));
+ break;
+ case DOUBLE:
+ result = set_double(double(value));
+ break;
+ case STRING:
+ case UNSPECIFIED:
+ result = set_string(value ? "true" : "false");
+ break;
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::setIntValue (int value)
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == INT)
+ return set_int(value);
+
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE || _type == UNSPECIFIED) {
+ clear_value();
+ _type = INT;
+ _local_val.int_val = 0;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setIntValue(value);
+ break;
+ case BOOL:
+ result = set_bool(value == 0 ? false : true);
+ break;
+ case INT:
+ result = set_int(value);
+ break;
+ case LONG:
+ result = set_long(long(value));
+ break;
+ case FLOAT:
+ result = set_float(float(value));
+ break;
+ case DOUBLE:
+ result = set_double(double(value));
+ break;
+ case STRING:
+ case UNSPECIFIED: {
+ char buf[128];
+ sprintf(buf, "%d", value);
+ result = set_string(buf);
+ break;
+ }
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::setLongValue (long value)
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == LONG)
+ return set_long(value);
+
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE || _type == UNSPECIFIED) {
+ clear_value();
+ _type = LONG;
+ _local_val.long_val = 0L;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setLongValue(value);
+ break;
+ case BOOL:
+ result = set_bool(value == 0L ? false : true);
+ break;
+ case INT:
+ result = set_int(int(value));
+ break;
+ case LONG:
+ result = set_long(value);
+ break;
+ case FLOAT:
+ result = set_float(float(value));
+ break;
+ case DOUBLE:
+ result = set_double(double(value));
+ break;
+ case STRING:
+ case UNSPECIFIED: {
+ char buf[128];
+ sprintf(buf, "%ld", value);
+ result = set_string(buf);
+ break;
+ }
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::setFloatValue (float value)
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == FLOAT)
+ return set_float(value);
+
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE || _type == UNSPECIFIED) {
+ clear_value();
+ _type = FLOAT;
+ _local_val.float_val = 0;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setFloatValue(value);
+ break;
+ case BOOL:
+ result = set_bool(value == 0.0 ? false : true);
+ break;
+ case INT:
+ result = set_int(int(value));
+ break;
+ case LONG:
+ result = set_long(long(value));
+ break;
+ case FLOAT:
+ result = set_float(value);
+ break;
+ case DOUBLE:
+ result = set_double(double(value));
+ break;
+ case STRING:
+ case UNSPECIFIED: {
+ char buf[128];
+ sprintf(buf, "%f", value);
+ result = set_string(buf);
+ break;
+ }
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::setDoubleValue (double value)
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == DOUBLE)
+ return set_double(value);
+
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE || _type == UNSPECIFIED) {
+ clear_value();
+ _local_val.double_val = value;
+ _type = DOUBLE;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setDoubleValue(value);
+ break;
+ case BOOL:
+ result = set_bool(value == 0.0L ? false : true);
+ break;
+ case INT:
+ result = set_int(int(value));
+ break;
+ case LONG:
+ result = set_long(long(value));
+ break;
+ case FLOAT:
+ result = set_float(float(value));
+ break;
+ case DOUBLE:
+ result = set_double(value);
+ break;
+ case STRING:
+ case UNSPECIFIED: {
+ char buf[128];
+ sprintf(buf, "%f", value);
+ result = set_string(buf);
+ break;
+ }
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::setStringValue (const char * value)
+{
+ // Shortcut for common case
+ if (_attr == (READ|WRITE) && _type == STRING)
+ return set_string(value);
+
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE || _type == UNSPECIFIED) {
+ clear_value();
+ _type = STRING;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setStringValue(value);
+ break;
+ case BOOL:
+ result = set_bool((compare_strings(value, "true")
+ || atoi(value)) ? true : false);
+ break;
+ case INT:
+ result = set_int(atoi(value));
+ break;
+ case LONG:
+ result = set_long(strtol(value, 0, 0));
+ break;
+ case FLOAT:
+ result = set_float(atof(value));
+ break;
+ case DOUBLE:
+ result = set_double(strtod(value, 0));
+ break;
+ case STRING:
+ case UNSPECIFIED:
+ result = set_string(value);
+ break;
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::setUnspecifiedValue (const char * value)
+{
+ bool result = false;
+ TEST_WRITE;
+ if (_type == NONE) {
+ clear_value();
+ _type = UNSPECIFIED;
+ }
+
+ switch (_type) {
+ case ALIAS:
+ result = _value.alias->setUnspecifiedValue(value);
+ break;
+ case BOOL:
+ result = set_bool((compare_strings(value, "true")
+ || atoi(value)) ? true : false);
+ break;
+ case INT:
+ result = set_int(atoi(value));
+ break;
+ case LONG:
+ result = set_long(strtol(value, 0, 0));
+ break;
+ case FLOAT:
+ result = set_float(atof(value));
+ break;
+ case DOUBLE:
+ result = set_double(strtod(value, 0));
+ break;
+ case STRING:
+ case UNSPECIFIED:
+ result = set_string(value);
+ break;
+ case NONE:
+ default:
+ break;
+ }
+
+ if (getAttribute(TRACE_WRITE))
+ trace_write();
+ return result;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<bool> &rawValue, bool useDefault)
+{
+ if (_type == ALIAS || _tied)
+ return false;
+
+ useDefault = useDefault && hasValue();
+ bool old_val = false;
+ if (useDefault)
+ old_val = getBoolValue();
+
+ clear_value();
+ _type = BOOL;
+ _tied = true;
+ _value.bool_val = rawValue.clone();
+
+ if (useDefault)
+ setBoolValue(old_val);
+
+ return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<int> &rawValue, bool useDefault)
+{
+ if (_type == ALIAS || _tied)
+ return false;
+
+ useDefault = useDefault && hasValue();
+ int old_val = 0;
+ if (useDefault)
+ old_val = getIntValue();
+
+ clear_value();
+ _type = INT;
+ _tied = true;
+ _value.int_val = rawValue.clone();
+
+ if (useDefault)
+ setIntValue(old_val);
+
+ return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<long> &rawValue, bool useDefault)
+{
+ if (_type == ALIAS || _tied)
+ return false;
+
+ useDefault = useDefault && hasValue();
+ long old_val = 0;
+ if (useDefault)
+ old_val = getLongValue();
+
+ clear_value();
+ _type = LONG;
+ _tied = true;
+ _value.long_val = rawValue.clone();
+
+ if (useDefault)
+ setLongValue(old_val);
+
+ return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<float> &rawValue, bool useDefault)
+{
+ if (_type == ALIAS || _tied)
+ return false;
+
+ useDefault = useDefault && hasValue();
+ float old_val = 0.0;
+ if (useDefault)
+ old_val = getFloatValue();
+
+ clear_value();
+ _type = FLOAT;
+ _tied = true;
+ _value.float_val = rawValue.clone();
+
+ if (useDefault)
+ setFloatValue(old_val);
+
+ return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<double> &rawValue, bool useDefault)
+{
+ if (_type == ALIAS || _tied)
+ return false;
+
+ useDefault = useDefault && hasValue();
+ double old_val = 0.0;
+ if (useDefault)
+ old_val = getDoubleValue();
+
+ clear_value();
+ _type = DOUBLE;
+ _tied = true;
+ _value.double_val = rawValue.clone();
+
+ if (useDefault)
+ setDoubleValue(old_val);
+
+ return true;
+
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<const char *> &rawValue, bool useDefault)
+{
+ if (_type == ALIAS || _tied)
+ return false;
+
+ useDefault = useDefault && hasValue();
+ string old_val;
+ if (useDefault)
+ old_val = getStringValue();
+
+ clear_value();
+ _type = STRING;
+ _tied = true;
+ _value.string_val = rawValue.clone();
+
+ if (useDefault)
+ setStringValue(old_val.c_str());
+
+ return true;
+}
+
+bool
+SGPropertyNode::untie ()
+{
+ if (!_tied)
+ return false;
+
+ switch (_type) {
+ case BOOL: {
+ bool val = getBoolValue();
+ clear_value();
+ _type = BOOL;
+ _local_val.bool_val = val;
+ break;
+ }
+ case INT: {
+ int val = getIntValue();
+ clear_value();
+ _type = INT;
+ _local_val.int_val = val;
+ break;
+ }
+ case LONG: {
+ long val = getLongValue();
+ clear_value();
+ _type = LONG;
+ _local_val.long_val = val;
+ break;
+ }
+ case FLOAT: {
+ float val = getFloatValue();
+ clear_value();
+ _type = FLOAT;
+ _local_val.float_val = val;
+ break;
+ }
+ case DOUBLE: {
+ double val = getDoubleValue();
+ clear_value();
+ _type = DOUBLE;
+ _local_val.double_val = val;
+ break;
+ }
+ case STRING:
+ case UNSPECIFIED: {
+ string val = getStringValue();
+ clear_value();
+ _type = STRING;
+ _local_val.string_val = copy_string(val.c_str());
+ break;
+ }
+ case NONE:
+ default:
+ break;
+ }
+
+ _tied = false;
+ return true;
+}
+
+SGPropertyNode *
+SGPropertyNode::getRootNode ()
+{
+ if (_parent == 0)
+ return this;
+ else
+ return _parent->getRootNode();
+}
+
+const SGPropertyNode *
+SGPropertyNode::getRootNode () const
+{
+ if (_parent == 0)
+ return this;
+ else
+ return _parent->getRootNode();
+}
+
+SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path, bool create)
+{
+ if (_path_cache == 0)
+ _path_cache = new hash_table;
+
+ SGPropertyNode * result = _path_cache->get(relative_path);
+ if (result == 0) {
+ vector<PathComponent> components;
+ parse_path(relative_path, components);
+ result = find_node(this, components, 0, create);
+ if (result != 0)
+ _path_cache->put(relative_path, result);
+ }
+
+ return result;
+}
+
+SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path, int index, bool create)
+{
+ vector<PathComponent> components;
+ parse_path(relative_path, components);
+ if (components.size() > 0)
+ components[components.size()-1].index = index;
+ return find_node(this, components, 0, create);
+}
+
+const SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path) const
+{
+ return ((SGPropertyNode *)this)->getNode(relative_path, false);
+}
+
+const SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path, int index) const
+{
+ return ((SGPropertyNode *)this)->getNode(relative_path, index, false);
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Convenience methods using relative paths.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Test whether another node has a value attached.
+ */
+bool
+SGPropertyNode::hasValue (const char * relative_path) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? false : node->hasValue());
+}
+
+
+/**
+ * Get the value type for another node.
+ */
+SGPropertyNode::Type
+SGPropertyNode::getType (const char * relative_path) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? UNSPECIFIED : (Type)(node->getType()));
+}
+
+
+/**
+ * Get a bool value for another node.
+ */
+bool
+SGPropertyNode::getBoolValue (const char * relative_path,
+ bool defaultValue) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? defaultValue : node->getBoolValue());
+}
+
+
+/**
+ * Get an int value for another node.
+ */
+int
+SGPropertyNode::getIntValue (const char * relative_path,
+ int defaultValue) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? defaultValue : node->getIntValue());
+}
+
+
+/**
+ * Get a long value for another node.
+ */
+long
+SGPropertyNode::getLongValue (const char * relative_path,
+ long defaultValue) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? defaultValue : node->getLongValue());
+}
+
+
+/**
+ * Get a float value for another node.
+ */
+float
+SGPropertyNode::getFloatValue (const char * relative_path,
+ float defaultValue) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? defaultValue : node->getFloatValue());
+}
+
+
+/**
+ * Get a double value for another node.
+ */
+double
+SGPropertyNode::getDoubleValue (const char * relative_path,
+ double defaultValue) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? defaultValue : node->getDoubleValue());
+}
+
+
+/**
+ * Get a string value for another node.
+ */
+const char *
+SGPropertyNode::getStringValue (const char * relative_path,
+ const char * defaultValue) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? defaultValue : node->getStringValue());
+}
+
+
+/**
+ * Set a bool value for another node.
+ */
+bool
+SGPropertyNode::setBoolValue (const char * relative_path, bool value)
+{
+ return getNode(relative_path, true)->setBoolValue(value);
+}
+
+
+/**
+ * Set an int value for another node.
+ */
+bool
+SGPropertyNode::setIntValue (const char * relative_path, int value)
+{
+ return getNode(relative_path, true)->setIntValue(value);
+}
+
+
+/**
+ * Set a long value for another node.
+ */
+bool
+SGPropertyNode::setLongValue (const char * relative_path, long value)
+{
+ return getNode(relative_path, true)->setLongValue(value);
+}
+
+
+/**
+ * Set a float value for another node.
+ */
+bool
+SGPropertyNode::setFloatValue (const char * relative_path, float value)
+{
+ return getNode(relative_path, true)->setFloatValue(value);
+}
+
+
+/**
+ * Set a double value for another node.
+ */
+bool
+SGPropertyNode::setDoubleValue (const char * relative_path, double value)
+{
+ return getNode(relative_path, true)->setDoubleValue(value);
+}
+
+
+/**
+ * Set a string value for another node.
+ */
+bool
+SGPropertyNode::setStringValue (const char * relative_path, const char * value)
+{
+ return getNode(relative_path, true)->setStringValue(value);
+}
+
+
+/**
+ * Set an unknown value for another node.
+ */
+bool
+SGPropertyNode::setUnspecifiedValue (const char * relative_path,
+ const char * value)
+{
+ return getNode(relative_path, true)->setUnspecifiedValue(value);
+}
+
+
+/**
+ * Test whether another node is tied.
+ */
+bool
+SGPropertyNode::isTied (const char * relative_path) const
+{
+ const SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? false : node->isTied());
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+ const SGRawValue<bool> &rawValue,
+ bool useDefault)
+{
+ return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+ const SGRawValue<int> &rawValue,
+ bool useDefault)
+{
+ return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * 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.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+ const SGRawValue<float> &rawValue,
+ bool useDefault)
+{
+ return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+ const SGRawValue<double> &rawValue,
+ bool useDefault)
+{
+ return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+ const SGRawValue<const char *> &rawValue,
+ bool useDefault)
+{
+ return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Attempt to untie another node reached by a relative path.
+ */
+bool
+SGPropertyNode::untie (const char * relative_path)
+{
+ SGPropertyNode * node = getNode(relative_path);
+ return (node == 0 ? false : node->untie());
+}
+
+void
+SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener)
+{
+ if (_listeners == 0)
+ _listeners = new vector<SGPropertyChangeListener *>;
+ _listeners->push_back(listener);
+ listener->register_property(this);
+}
+
+void
+SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
+{
+ vector<SGPropertyChangeListener *>::iterator it =
+ find(_listeners->begin(), _listeners->end(), listener);
+ if (it != _listeners->end()) {
+ _listeners->erase(it);
+ listener->unregister_property(this);
+ if (_listeners->empty()) {
+ vector<SGPropertyChangeListener *> * tmp = _listeners;
+ _listeners = 0;
+ delete tmp;
+ }
+ }
+}
+
+void
+SGPropertyNode::fireValueChanged ()
+{
+ fireValueChanged(this);
+}
+
+void
+SGPropertyNode::fireChildAdded (SGPropertyNode * child)
+{
+ fireChildAdded(this, child);
+}
+
+void
+SGPropertyNode::fireChildRemoved (SGPropertyNode * child)
+{
+ fireChildRemoved(this, child);
+}
+
+void
+SGPropertyNode::fireValueChanged (SGPropertyNode * node)
+{
+ if (_listeners != 0) {
+ for (unsigned int i = 0; i < _listeners->size(); i++) {
+ (*_listeners)[i]->valueChanged(node);
+ }
+ }
+ if (_parent != 0)
+ _parent->fireValueChanged(node);
+}
+
+void
+SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
+ SGPropertyNode * child)
+{
+ if (_listeners != 0) {
+ for (unsigned int i = 0; i < _listeners->size(); i++) {
+ (*_listeners)[i]->childAdded(parent, child);
+ }
+ }
+ if (_parent != 0)
+ _parent->fireChildAdded(parent, child);
+}
+
+void
+SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
+ SGPropertyNode * child)
+{
+ if (_listeners != 0) {
+ for (unsigned int i = 0; i < _listeners->size(); i++) {
+ (*_listeners)[i]->childRemoved(parent, child);
+ }
+ }
+ if (_parent != 0)
+ _parent->fireChildRemoved(parent, child);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Simplified hash table for caching paths.
+////////////////////////////////////////////////////////////////////////
+
+#define HASH_TABLE_SIZE 199
+
+SGPropertyNode::hash_table::entry::entry ()
+ : _key(0),
+ _value(0)
+{
+}
+
+SGPropertyNode::hash_table::entry::~entry ()
+{
+ // Don't delete the value; we don't own
+ // the pointer.
+ delete [] _key;
+}
+
+void
+SGPropertyNode::hash_table::entry::set_key (const char * key)
+{
+ _key = copy_string(key);
+}
+
+void
+SGPropertyNode::hash_table::entry::set_value (SGPropertyNode * value)
+{
+ _value = value;
+}
+
+SGPropertyNode::hash_table::bucket::bucket ()
+ : _length(0),
+ _entries(0)
+{
+}
+
+SGPropertyNode::hash_table::bucket::~bucket ()
+{
+ for (int i = 0; i < _length; i++)
+ delete _entries[i];
+}
+
+SGPropertyNode::hash_table::entry *
+SGPropertyNode::hash_table::bucket::get_entry (const char * key, bool create)
+{
+ int i;
+ for (i = 0; i < _length; i++) {
+ if (!strcmp(_entries[i]->get_key(), key))
+ return _entries[i];
+ }
+ if (create) {
+ entry ** new_entries = new entry*[_length+1];
+ for (i = 0; i < _length; i++) {
+ new_entries[i] = _entries[i];
+ }
+ delete [] _entries;
+ _entries = new_entries;
+ _entries[_length] = new entry;
+ _entries[_length]->set_key(key);
+ _length++;
+ return _entries[_length - 1];
+ } else {
+ return 0;
+ }
+}
+
+
+SGPropertyNode::hash_table::hash_table ()
+ : _data_length(0),
+ _data(0)
+{
+}
+
+SGPropertyNode::hash_table::~hash_table ()
+{
+ for (unsigned int i = 0; i < _data_length; i++)
+ delete _data[i];
+}
+
+SGPropertyNode *
+SGPropertyNode::hash_table::get (const char * key)
+{
+ if (_data_length == 0)
+ return 0;
+ unsigned int index = hashcode(key) % _data_length;
+ if (_data[index] == 0)
+ return 0;
+ entry * e = _data[index]->get_entry(key);
+ if (e == 0)
+ return 0;
+ else
+ return e->get_value();
+}
+
+void
+SGPropertyNode::hash_table::put (const char * key, SGPropertyNode * value)
+{
+ if (_data_length == 0) {
+ _data = new bucket*[HASH_TABLE_SIZE];
+ _data_length = HASH_TABLE_SIZE;
+ for (unsigned int i = 0; i < HASH_TABLE_SIZE; i++)
+ _data[i] = 0;
+ }
+ unsigned int index = hashcode(key) % _data_length;
+ if (_data[index] == 0) {
+ _data[index] = new bucket;
+ }
+ entry * e = _data[index]->get_entry(key, true);
+ e->set_value(value);
+}
+
+unsigned int
+SGPropertyNode::hash_table::hashcode (const char * key)
+{
+ unsigned int hash = 0;
+ while (*key != 0) {
+ hash = 31 * hash + *key;
+ key++;
+ }
+ return hash;
+}
+
+
+
+/**
+ * Default constructor
+ */
+SGPropertyNode_ptr::SGPropertyNode_ptr()
+{
+ _ptr = 0;
+}
+
+/**
+ * Copy constructor
+ */
+SGPropertyNode_ptr::SGPropertyNode_ptr( const SGPropertyNode_ptr &r )
+{
+ _ptr = r._ptr;
+ if (_ptr)
+ _ptr->incrementRef();
+}
+
+/**
+ * Constructor from a pointer to a node
+ */
+SGPropertyNode_ptr::SGPropertyNode_ptr( SGPropertyNode *p )
+{
+ _ptr = p;
+ if (_ptr)
+ _ptr->incrementRef();
+}
+
+/**
+ * Destructor
+ */
+SGPropertyNode_ptr::~SGPropertyNode_ptr()
+{
+ if (_ptr && _ptr->decrementRef() == 0)
+ delete _ptr;
+}
+
+/**
+ * Assignement operator
+ */
+SGPropertyNode_ptr &
+SGPropertyNode_ptr::operator=( const SGPropertyNode_ptr &r )
+{
+ if (_ptr && _ptr->decrementRef() == 0)
+ delete _ptr;
+ _ptr = r._ptr;
+ if (_ptr)
+ _ptr->incrementRef();
+
+ return *this;
+}
+
+/**
+ * Pointer access operator
+ */
+SGPropertyNode *
+SGPropertyNode_ptr::operator->()
+{
+ return _ptr;
+}
+
+/**
+ * Pointer access operator (const)
+ */
+const SGPropertyNode *
+SGPropertyNode_ptr::operator->() const
+{
+ return _ptr;
+}
+
+/**
+ * Conversion to SGPropertyNode * operator
+ */
+SGPropertyNode_ptr::operator SGPropertyNode *()
+{
+ return _ptr;
+}
+
+/**
+ * Conversion to const SGPropertyNode * operator
+ */
+SGPropertyNode_ptr::operator const SGPropertyNode *() const
+{
+ return _ptr;
+}
+
+/**
+ * Validity test
+ */
+bool
+SGPropertyNode_ptr::valid() const
+{
+ return _ptr != 0;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of SGPropertyChangeListener.
+////////////////////////////////////////////////////////////////////////
+
+SGPropertyChangeListener::~SGPropertyChangeListener ()
+{
+ // This will come back and remove
+ // the current item each time. Is
+ // that OK?
+ vector<SGPropertyNode *>::iterator it;
+ for (it = _properties.begin(); it != _properties.end(); it++)
+ (*it)->removeChangeListener(this);
+}
+
+void
+SGPropertyChangeListener::valueChanged (SGPropertyNode * node)
+{
+ // NO-OP
+}
+
+void
+SGPropertyChangeListener::childAdded (SGPropertyNode * node,
+ SGPropertyNode * child)
+{
+ // NO-OP
+}
+
+void
+SGPropertyChangeListener::childRemoved (SGPropertyNode * parent,
+ SGPropertyNode * child)
+{
+ // NO-OP
+}
+
+void
+SGPropertyChangeListener::register_property (SGPropertyNode * node)
+{
+ _properties.push_back(node);
+}
+
+void
+SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
+{
+ vector<SGPropertyNode *>::iterator it =
+ find(_properties.begin(), _properties.end(), node);
+ if (it != _properties.end())
+ _properties.erase(it);
+}
+
+
+// end of props.cxx
--- /dev/null
+/**
+ * \file props.hxx
+ * Interface definition for a property list.
+ * Started Fall 2000 by David Megginson, david@megginson.com
+ * This code is released into the Public Domain.
+ *
+ * See props.html for documentation [replace with URL when available].
+ *
+ * $Id$
+ */
+
+#ifndef __PROPS_HXX
+#define __PROPS_HXX
+
+#ifndef PROPS_STANDALONE
+#define PROPS_STANDALONE 0
+#endif
+
+#include <vector>
+
+#if PROPS_STANDALONE
+
+#include <string>
+#include <iostream>
+
+using std::string;
+using std::vector;
+using std::istream;
+using std::ostream;
+
+#else
+
+#include <simgear/compiler.h>
+#include <simgear/debug/logstream.hxx>
+#include STL_STRING
+#include STL_IOSTREAM
+SG_USING_STD(string);
+SG_USING_STD(vector);
+SG_USING_STD(istream);
+SG_USING_STD(ostream);
+
+#endif
+
+
+#ifdef NONE
+#pragma warn A sloppy coder has defined NONE as a macro!
+#undef NONE
+#endif
+
+#ifdef ALIAS
+#pragma warn A sloppy coder has defined ALIAS as a macro!
+#undef ALIAS
+#endif
+
+#ifdef UNSPECIFIED
+#pragma warn A sloppy coder has defined UNSPECIFIED as a macro!
+#undef UNSPECIFIED
+#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 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
+#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
+////////////////////////////////////////////////////////////////////////
+// A raw value.
+//
+// This is the mechanism that information-providing routines can
+// use to link their own values to the property manager. Any
+// SGValue can be tied to a raw value and then untied again.
+//
+// Note: we are forced to use inlined methods here to ensure
+// that the templates will be instantiated. We're probably taking
+// a small performance hit for that.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Abstract base class for a raw value.
+ *
+ * <p>The property manager is implemented in two layers. The {@link
+ * SGPropertyNode} is the highest and most abstract layer,
+ * representing an LValue/RValue pair: it records the position of the
+ * property in the property tree and contains facilities for
+ * navigation to other nodes. It is guaranteed to be persistent: the
+ * {@link SGPropertyNode} will not change during a session, even if
+ * the property is bound and unbound multiple times.</p>
+ *
+ * <p>When the property value is not managed internally in the
+ * SGPropertyNode, the SGPropertyNode will contain a reference to an
+ * SGRawValue (this class), which provides an abstract way to get,
+ * set, and clone the underlying value. The SGRawValue may change
+ * frequently during a session as a value is retyped or bound and
+ * unbound to various data source, but the abstract SGPropertyNode
+ * layer insulates the application from those changes. The raw value
+ * contains no facilities for data binding or for type conversion: it
+ * is simply the abstraction of a primitive data type (or a compound
+ * data type, in the case of a string).</p>
+ *
+ * <p>The SGPropertyNode class always keeps a *copy* of a raw value,
+ * not the original one passed to it; if you override a derived class
+ * but do not replace the {@link #clone} method, strange things will
+ * happen.</p>
+ *
+ * <p>All derived SGRawValue classes must implement {@link #getValue},
+ * {@link #setValue}, and {@link #clone} for the appropriate type.</p>
+ *
+ * @see SGPropertyNode
+ * @see SGRawValuePointer
+ * @see SGRawValueFunctions
+ * @see SGRawValueFunctionsIndexed
+ * @see SGRawValueMethods
+ * @see SGRawValueMethodsIndexed
+ */
+template <class T>
+class SGRawValue
+{
+public:
+
+ /**
+ * The default underlying value for this type.
+ *
+ * Every raw value has a default; the default is false for a
+ * boolean, 0 for the various numeric values, and "" for a string.
+ * If additional types of raw values are added in the future, they
+ * may need different kinds of default values (such as epoch for a
+ * date type). The default value is used when creating new values.
+ */
+ static const T DefaultValue; // Default for this kind of raw value.
+
+
+ /**
+ * Constructor.
+ *
+ * Use the default value for this type.
+ */
+ SGRawValue () {}
+
+
+ /**
+ * Destructor.
+ */
+ virtual ~SGRawValue () {}
+
+
+ /**
+ * Return the underlying value.
+ *
+ * @return The actual value for the property.
+ * @see #setValue
+ */
+ virtual T getValue () const = 0;
+
+
+ /**
+ * Assign a new underlying value.
+ *
+ * If the new value cannot be set (because this is a read-only
+ * raw value, or because the new value is not acceptable for
+ * some reason) this method returns false and leaves the original
+ * value unchanged.
+ *
+ * @param value The actual value for the property.
+ * @return true if the value was set successfully, false otherwise.
+ * @see #getValue
+ */
+ virtual bool setValue (T value) = 0;
+
+
+ /**
+ * Create a new deep copy of this raw value.
+ *
+ * The copy will contain its own version of the underlying value
+ * as well.
+ *
+ * @return A deep copy of the current object.
+ */
+ virtual SGRawValue * clone () const = 0;
+};
+
+
+/**
+ * A raw value bound to a pointer.
+ *
+ * This is the most efficient way to tie an external value, but also
+ * the most dangerous, because there is no way for the supplier to
+ * perform bounds checking and derived calculations except by polling
+ * the variable to see if it has changed. There is no default
+ * constructor, because this class would be meaningless without a
+ * pointer.
+ */
+template <class T>
+class SGRawValuePointer : public SGRawValue<T>
+{
+public:
+
+ /**
+ * Explicit pointer constructor.
+ *
+ * Create a new raw value bound to the value of the variable
+ * referenced by the pointer.
+ *
+ * @param ptr The pointer to the variable to which this raw value
+ * will be bound.
+ */
+ SGRawValuePointer (T * ptr) : _ptr(ptr) {}
+
+ /**
+ * Destructor.
+ */
+ virtual ~SGRawValuePointer () {}
+
+ /**
+ * Get the underlying value.
+ *
+ * This method will dereference the pointer and return the
+ * variable's value.
+ */
+ virtual T getValue () const { return *_ptr; }
+
+ /**
+ * Set the underlying value.
+ *
+ * This method will dereference the pointer and change the
+ * variable's value.
+ */
+ virtual bool setValue (T value) { *_ptr = value; return true; }
+
+ /**
+ * Create a copy of this raw value.
+ *
+ * The copy will use the same external pointer as the original.
+ */
+ virtual SGRawValue<T> * clone () const {
+ return new SGRawValuePointer<T>(_ptr);
+ }
+
+private:
+ T * _ptr;
+};
+
+
+/**
+ * A value managed through static functions.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.
+ */
+template <class T>
+class SGRawValueFunctions : public SGRawValue<T>
+{
+public:
+
+ /**
+ * The template type of a static getter function.
+ */
+ typedef T (*getter_t)();
+
+ /**
+ * The template type of a static setter function.
+ */
+ typedef void (*setter_t)(T);
+
+ /**
+ * Explicit constructor.
+ *
+ * Create a new raw value bound to the getter and setter supplied.
+ *
+ * @param getter A static function for getting a value, or 0
+ * to read-disable the value.
+ * @param setter A static function for setting a value, or 0
+ * to write-disable the value.
+ */
+ SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0)
+ : _getter(getter), _setter(setter) {}
+
+ /**
+ * Destructor.
+ */
+ virtual ~SGRawValueFunctions () {}
+
+ /**
+ * Get the underlying value.
+ *
+ * This method will invoke the getter function to get a value.
+ * If no getter function was supplied, this method will always
+ * return the default value for the type.
+ */
+ virtual T getValue () const {
+ if (_getter) return (*_getter)();
+ else return SGRawValue<T>::DefaultValue;
+ }
+
+ /**
+ * Set the underlying value.
+ *
+ * This method will invoke the setter function to change the
+ * underlying value. If no setter function was supplied, this
+ * method will return false.
+ */
+ virtual bool setValue (T value) {
+ if (_setter) { (*_setter)(value); return true; }
+ else return false;
+ }
+
+ /**
+ * Create a copy of this raw value, bound to the same functions.
+ */
+ virtual SGRawValue<T> * clone () const {
+ return new SGRawValueFunctions<T>(_getter,_setter);
+ }
+
+private:
+ getter_t _getter;
+ setter_t _setter;
+};
+
+
+/**
+ * An indexed value bound to static functions.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter. An indexed value is useful for binding one
+ * of a list of possible values (such as multiple engines for a
+ * plane). The index is hard-coded at creation time.
+ *
+ * @see SGRawValue
+ */
+template <class T>
+class SGRawValueFunctionsIndexed : public SGRawValue<T>
+{
+public:
+ typedef T (*getter_t)(int);
+ typedef void (*setter_t)(int,T);
+ SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0)
+ : _index(index), _getter(getter), _setter(setter) {}
+ virtual ~SGRawValueFunctionsIndexed () {}
+ virtual T getValue () const {
+ if (_getter) return (*_getter)(_index);
+ else return SGRawValue<T>::DefaultValue;
+ }
+ virtual bool setValue (T value) {
+ if (_setter) { (*_setter)(_index, value); return true; }
+ else return false;
+ }
+ virtual SGRawValue<T> * clone () const {
+ return new SGRawValueFunctionsIndexed<T>(_index, _getter, _setter);
+ }
+private:
+ int _index;
+ getter_t _getter;
+ setter_t _setter;
+};
+
+
+/**
+ * A value managed through an object and access methods.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.
+ */
+template <class C, class T>
+class SGRawValueMethods : public SGRawValue<T>
+{
+public:
+ typedef T (C::*getter_t)() const;
+ typedef void (C::*setter_t)(T);
+ SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0)
+ : _obj(obj), _getter(getter), _setter(setter) {}
+ virtual ~SGRawValueMethods () {}
+ virtual T getValue () const {
+ if (_getter) { return (_obj.*_getter)(); }
+ else { return SGRawValue<T>::DefaultValue; }
+ }
+ virtual bool setValue (T value) {
+ if (_setter) { (_obj.*_setter)(value); return true; }
+ else return false;
+ }
+ virtual SGRawValue<T> * clone () const {
+ return new SGRawValueMethods<C,T>(_obj, _getter, _setter);
+ }
+private:
+ C &_obj;
+ getter_t _getter;
+ setter_t _setter;
+};
+
+
+/**
+ * An indexed value managed through an object and access methods.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.
+ */
+template <class C, class T>
+class SGRawValueMethodsIndexed : public SGRawValue<T>
+{
+public:
+ typedef T (C::*getter_t)(int) const;
+ typedef void (C::*setter_t)(int, T);
+ SGRawValueMethodsIndexed (C &obj, int index,
+ getter_t getter = 0, setter_t setter = 0)
+ : _obj(obj), _index(index), _getter(getter), _setter(setter) {}
+ virtual ~SGRawValueMethodsIndexed () {}
+ virtual T getValue () const {
+ if (_getter) { return (_obj.*_getter)(_index); }
+ else { return SGRawValue<T>::DefaultValue; }
+ }
+ virtual bool setValue (T value) {
+ if (_setter) { (_obj.*_setter)(_index, value); return true; }
+ else return false;
+ }
+ virtual SGRawValue<T> * clone () const {
+ return new SGRawValueMethodsIndexed<C,T>(_obj, _index, _getter, _setter);
+ }
+private:
+ C &_obj;
+ int _index;
+ getter_t _getter;
+ setter_t _setter;
+};
+
+\f
+/**
+ * The smart pointer that manage reference counting
+ */
+class SGPropertyNode;
+class SGPropertyNode_ptr
+{
+public:
+
+ /**
+ * Default constructor
+ */
+ SGPropertyNode_ptr();
+
+ /**
+ * Copy constructor
+ */
+ SGPropertyNode_ptr( const SGPropertyNode_ptr &r );
+
+ /**
+ * Constructor from a pointer to a node
+ */
+ SGPropertyNode_ptr( SGPropertyNode *p );
+
+ /**
+ * Destructor
+ */
+ ~SGPropertyNode_ptr();
+
+ /**
+ * Assignement operator
+ */
+ SGPropertyNode_ptr &operator=( const SGPropertyNode_ptr &r );
+
+ /**
+ * Pointer access operator
+ */
+ SGPropertyNode *operator->();
+
+ /**
+ * Pointer access operator (const)
+ */
+ const SGPropertyNode *operator->() const;
+
+ /**
+ * Conversion to SGPropertyNode * operator
+ */
+ operator SGPropertyNode *();
+
+ /**
+ * Conversion to const SGPropertyNode * operator
+ */
+ operator const SGPropertyNode *() const;
+
+ /**
+ * Return the pointer.
+ */
+ SGPropertyNode * ptr () { return _ptr; }
+
+ /**
+ * Validity test
+ */
+ bool valid() const;
+
+private:
+
+ SGPropertyNode *_ptr;
+};
+
+
+\f
+/**
+ * The property change listener interface.
+ *
+ * <p>Any class that needs to listen for property changes must implement
+ * this interface.</p>
+ */
+class SGPropertyChangeListener
+{
+public:
+ virtual ~SGPropertyChangeListener ();
+ virtual void valueChanged (SGPropertyNode * node);
+ virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child);
+ virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child);
+
+protected:
+ friend class SGPropertyNode;
+ virtual void register_property (SGPropertyNode * node);
+ virtual void unregister_property (SGPropertyNode * node);
+
+private:
+ vector<SGPropertyNode *> _properties;
+};
+
+
+\f
+/**
+ * A node in a property tree.
+ */
+class SGPropertyNode
+{
+public:
+
+ /**
+ * Public constants.
+ */
+ enum {
+ MAX_STRING_LEN = 1024
+ };
+
+ /**
+ * Property value types.
+ */
+ enum Type {
+ NONE,
+ ALIAS,
+ BOOL,
+ INT,
+ LONG,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ UNSPECIFIED
+ };
+
+
+ /**
+ * Access mode attributes.
+ *
+ * <p>The ARCHIVE attribute is strictly advisory, and controls
+ * whether the property should normally be saved and restored.</p>
+ */
+ enum Attribute {
+ READ = 1,
+ WRITE = 2,
+ ARCHIVE = 4,
+ REMOVED = 8,
+ TRACE_READ = 16,
+ TRACE_WRITE = 32
+ };
+
+
+ /**
+ * Last used attribute
+ * Update as needed when enum Attribute is changed
+ */
+ static const int LAST_USED_ATTRIBUTE;
+
+
+ /**
+ * Default constructor.
+ */
+ SGPropertyNode ();
+
+
+ /**
+ * Copy constructor.
+ */
+ SGPropertyNode (const SGPropertyNode &node);
+
+
+ /**
+ * Destructor.
+ */
+ virtual ~SGPropertyNode ();
+
+
+
+ //
+ // Basic properties.
+ //
+
+ /**
+ * Test whether this node contains a primitive leaf value.
+ */
+ bool hasValue () const { return (_type != NONE); }
+
+
+ /**
+ * Get the node's simple (XML) name.
+ */
+ const char * getName () const { return _name; }
+
+
+ /**
+ * Get the node's pretty display name, with subscript when needed.
+ */
+ const char * getDisplayName (bool simplify = false) const;
+
+
+ /**
+ * Get the node's integer index.
+ */
+ int getIndex () const { return _index; }
+
+
+ /**
+ * Get a non-const pointer to the node's parent.
+ */
+ SGPropertyNode * getParent () { return _parent; }
+
+
+ /**
+ * Get a const pointer to the node's parent.
+ */
+ const SGPropertyNode * getParent () const { return _parent; }
+
+
+ //
+ // Children.
+ //
+
+
+ /**
+ * Get the number of child nodes.
+ */
+ int nChildren () const { return _children.size(); }
+
+
+ /**
+ * Get a child node by position (*NOT* index).
+ */
+ SGPropertyNode * getChild (int position);
+
+
+ /**
+ * Get a const child node by position (*NOT* index).
+ */
+ const SGPropertyNode * getChild (int position) const;
+
+
+ /**
+ * Test whether a named child exists.
+ */
+ bool hasChild (const char * name, int index = 0) const
+ {
+ return (getChild(name, index) != 0);
+ }
+
+
+ /**
+ * Get a child node by name and index.
+ */
+ SGPropertyNode * getChild (const char * name, int index = 0,
+ bool create = false);
+
+
+ /**
+ * Get a const child node by name and index.
+ */
+ const SGPropertyNode * getChild (const char * name, int index = 0) const;
+
+
+ /**
+ * Get a vector of all children with the specified name.
+ */
+ vector<SGPropertyNode_ptr> getChildren (const char * name) const;
+
+
+ /**
+ * Remove a child node
+ */
+ SGPropertyNode_ptr removeChild (const char * name, int index = 0,
+ bool keep = true);
+
+
+ //
+ // Alias support.
+ //
+
+
+ /**
+ * Alias this node's leaf value to another's.
+ */
+ bool alias (SGPropertyNode * target);
+
+
+ /**
+ * Alias this node's leaf value to another's by relative path.
+ */
+ bool alias (const char * path);
+
+
+ /**
+ * Remove any alias for this node.
+ */
+ bool unalias ();
+
+
+ /**
+ * Test whether the node's leaf value is aliased to another's.
+ */
+ bool isAlias () const { return (_type == ALIAS); }
+
+
+ /**
+ * Get a non-const pointer to the current alias target, if any.
+ */
+ SGPropertyNode * getAliasTarget ();
+
+
+ /**
+ * Get a const pointer to the current alias target, if any.
+ */
+ const SGPropertyNode * getAliasTarget () const;
+
+
+ //
+ // Path information.
+ //
+
+
+ /**
+ * Get the path to this node from the root.
+ */
+ const char * getPath (bool simplify = false) const;
+
+
+ /**
+ * Get a pointer to the root node.
+ */
+ SGPropertyNode * getRootNode ();
+
+
+ /**
+ * Get a const pointer to the root node.
+ */
+ const SGPropertyNode * getRootNode () const;
+
+
+ /**
+ * Get a pointer to another node by relative path.
+ */
+ SGPropertyNode * getNode (const char * relative_path, bool create = false);
+
+
+ /**
+ * Get a pointer to another node by relative path.
+ *
+ * This method leaves the index off the last member of the path,
+ * so that the user can specify it separately (and save some
+ * string building). For example, getNode("/bar[1]/foo", 3) is
+ * exactly equivalent to getNode("bar[1]/foo[3]"). The index
+ * provided overrides any given in the path itself for the last
+ * component.
+ */
+ SGPropertyNode * getNode (const char * relative_path, int index,
+ bool create = false);
+
+
+ /**
+ * Get a const pointer to another node by relative path.
+ */
+ const SGPropertyNode * getNode (const char * relative_path) const;
+
+
+ /**
+ * Get a const pointer to another node by relative path.
+ *
+ * This method leaves the index off the last member of the path,
+ * so that the user can specify it separate.
+ */
+ const SGPropertyNode * getNode (const char * relative_path,
+ int index) const;
+
+
+ //
+ // Access Mode.
+ //
+
+ /**
+ * Check a single mode attribute for the property node.
+ */
+ bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); }
+
+
+ /**
+ * Set a single mode attribute for the property node.
+ */
+ void setAttribute (Attribute attr, bool state) {
+ (state ? _attr |= attr : _attr &= ~attr);
+ }
+
+
+ /**
+ * Get all of the mode attributes for the property node.
+ */
+ int getAttributes () const { return _attr; }
+
+
+ /**
+ * Set all of the mode attributes for the property node.
+ */
+ void setAttributes (int attr) { _attr = attr; }
+
+
+ //
+ // Leaf Value (primitive).
+ //
+
+
+ /**
+ * Get the type of leaf value, if any, for this node.
+ */
+ Type getType () const;
+
+
+ /**
+ * Get a bool value for this node.
+ */
+ bool getBoolValue () const;
+
+
+ /**
+ * Get an int value for this node.
+ */
+ int getIntValue () const;
+
+
+ /**
+ * Get a long int value for this node.
+ */
+ long getLongValue () const;
+
+
+ /**
+ * Get a float value for this node.
+ */
+ float getFloatValue () const;
+
+
+ /**
+ * Get a double value for this node.
+ */
+ double getDoubleValue () const;
+
+
+ /**
+ * Get a string value for this node.
+ */
+ const char * getStringValue () const;
+
+
+
+ /**
+ * Set a bool value for this node.
+ */
+ bool setBoolValue (bool value);
+
+
+ /**
+ * Set an int value for this node.
+ */
+ bool setIntValue (int value);
+
+
+ /**
+ * Set a long int value for this node.
+ */
+ bool setLongValue (long value);
+
+
+ /**
+ * Set a float value for this node.
+ */
+ bool setFloatValue (float value);
+
+
+ /**
+ * Set a double value for this node.
+ */
+ bool setDoubleValue (double value);
+
+
+ /**
+ * Set a string value for this node.
+ */
+ bool setStringValue (const char * value);
+
+
+ /**
+ * Set a value of unspecified type for this node.
+ */
+ bool setUnspecifiedValue (const char * value);
+
+
+ //
+ // Data binding.
+ //
+
+
+ /**
+ * Test whether this node is bound to an external data source.
+ */
+ bool isTied () const { return _tied; }
+
+
+ /**
+ * Bind this node to an external bool source.
+ */
+ bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
+
+
+ /**
+ * Bind this node to an external int source.
+ */
+ bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
+
+
+ /**
+ * Bind this node to an external long int source.
+ */
+ bool tie (const SGRawValue<long> &rawValue, bool useDefault = true);
+
+
+ /**
+ * Bind this node to an external float source.
+ */
+ bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
+
+
+ /**
+ * Bind this node to an external double source.
+ */
+ bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
+
+
+ /**
+ * Bind this node to an external string source.
+ */
+ bool tie (const SGRawValue<const char *> &rawValue, bool useDefault = true);
+
+
+ /**
+ * Unbind this node from any external data source.
+ */
+ bool untie ();
+
+
+ //
+ // Convenience methods using paths.
+ // TODO: add attribute methods
+ //
+
+
+ /**
+ * Get another node's type.
+ */
+ Type getType (const char * relative_path) const;
+
+
+ /**
+ * Test whether another node has a leaf value.
+ */
+ bool hasValue (const char * relative_path) const;
+
+
+ /**
+ * Get another node's value as a bool.
+ */
+ bool getBoolValue (const char * relative_path,
+ bool defaultValue = false) const;
+
+
+ /**
+ * Get another node's value as an int.
+ */
+ int getIntValue (const char * relative_path,
+ int defaultValue = 0) const;
+
+
+ /**
+ * Get another node's value as a long int.
+ */
+ long getLongValue (const char * relative_path,
+ long defaultValue = 0L) const;
+
+
+ /**
+ * Get another node's value as a float.
+ */
+ float getFloatValue (const char * relative_path,
+ float defaultValue = 0.0) const;
+
+
+ /**
+ * Get another node's value as a double.
+ */
+ double getDoubleValue (const char * relative_path,
+ double defaultValue = 0.0L) const;
+
+
+ /**
+ * Get another node's value as a string.
+ */
+ const char * getStringValue (const char * relative_path,
+ const char * defaultValue = "") const;
+
+
+ /**
+ * Set another node's value as a bool.
+ */
+ bool setBoolValue (const char * relative_path, bool value);
+
+
+ /**
+ * Set another node's value as an int.
+ */
+ bool setIntValue (const char * relative_path, int value);
+
+
+ /**
+ * Set another node's value as a long int.
+ */
+ bool setLongValue (const char * relative_path, long value);
+
+
+ /**
+ * Set another node's value as a float.
+ */
+ bool setFloatValue (const char * relative_path, float value);
+
+
+ /**
+ * Set another node's value as a double.
+ */
+ bool setDoubleValue (const char * relative_path, double value);
+
+
+ /**
+ * Set another node's value as a string.
+ */
+ bool setStringValue (const char * relative_path, const char * value);
+
+
+ /**
+ * Set another node's value with no specified type.
+ */
+ bool setUnspecifiedValue (const char * relative_path, const char * value);
+
+
+ /**
+ * Test whether another node is bound to an external data source.
+ */
+ bool isTied (const char * relative_path) const;
+
+
+ /**
+ * Bind another node to an external bool source.
+ */
+ bool tie (const char * relative_path, const SGRawValue<bool> &rawValue,
+ bool useDefault = true);
+
+
+ /**
+ * Bind another node to an external int source.
+ */
+ bool tie (const char * relative_path, const SGRawValue<int> &rawValue,
+ bool useDefault = true);
+
+
+ /**
+ * Bind another node to an external long int source.
+ */
+ bool tie (const char * relative_path, const SGRawValue<long> &rawValue,
+ bool useDefault = true);
+
+
+ /**
+ * Bind another node to an external float source.
+ */
+ bool tie (const char * relative_path, const SGRawValue<float> &rawValue,
+ bool useDefault = true);
+
+
+ /**
+ * Bind another node to an external double source.
+ */
+ bool tie (const char * relative_path, const SGRawValue<double> &rawValue,
+ bool useDefault = true);
+
+
+ /**
+ * Bind another node to an external string source.
+ */
+ bool tie (const char * relative_path, const SGRawValue<const char *> &rawValue,
+ bool useDefault = true);
+
+
+ /**
+ * Unbind another node from any external data source.
+ */
+ bool untie (const char * relative_path);
+
+
+ /**
+ * Add a change listener to the property.
+ */
+ void addChangeListener (SGPropertyChangeListener * listener);
+
+
+ /**
+ * Remove a change listener from the property.
+ */
+ void removeChangeListener (SGPropertyChangeListener * listener);
+
+
+ /**
+ * Fire a value change event to all listeners.
+ */
+ void fireValueChanged ();
+
+
+ /**
+ * Fire a child-added event to all listeners.
+ */
+ void fireChildAdded (SGPropertyNode * child);
+
+
+ /**
+ * Fire a child-removed event to all listeners.
+ */
+ void fireChildRemoved (SGPropertyNode * child);
+
+
+protected:
+
+ void fireValueChanged (SGPropertyNode * node);
+ void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child);
+ void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child);
+
+ /**
+ * Protected constructor for making new nodes on demand.
+ */
+ SGPropertyNode (const char * name, int index, SGPropertyNode * parent);
+
+
+private:
+
+ // Get the raw value
+ bool get_bool () const;
+ int get_int () const;
+ long get_long () const;
+ float get_float () const;
+ double get_double () const;
+ const char * get_string () const;
+
+ // Set the raw value
+ bool set_bool (bool value);
+ bool set_int (int value);
+ bool set_long (long value);
+ bool set_float (float value);
+ bool set_double (double value);
+ bool set_string (const char * value);
+
+
+ /**
+ * Clear any existing value and set the type to NONE.
+ */
+ void clear_value ();
+
+
+ /**
+ * Get the value as a string.
+ */
+ const char * make_string () const;
+
+
+ /**
+ * Trace a read access.
+ */
+ void trace_read () const;
+
+
+ /**
+ * Trace a write access.
+ */
+ void trace_write () const;
+
+
+ /**
+ * Increment reference counter
+ */
+ void incrementRef();
+
+ /**
+ * Decrement reference counter
+ */
+ int decrementRef();
+
+ friend class SGPropertyNode_ptr;
+
+
+ mutable char _buffer[MAX_STRING_LEN+1];
+
+ class hash_table;
+
+ char * _name;
+ mutable char * _display_name;
+ int _index;
+ SGPropertyNode * _parent;
+ vector<SGPropertyNode_ptr> _children;
+ vector<SGPropertyNode_ptr> _removedChildren;
+ mutable char * _path;
+ hash_table * _path_cache;
+ Type _type;
+ bool _tied;
+ int _attr;
+ int _count;
+
+ // The right kind of pointer...
+ union {
+ SGPropertyNode * alias;
+ SGRawValue<bool> * bool_val;
+ SGRawValue<int> * int_val;
+ SGRawValue<long> * long_val;
+ SGRawValue<float> * float_val;
+ SGRawValue<double> * double_val;
+ SGRawValue<const char *> * string_val;
+ } _value;
+
+ union {
+ bool bool_val;
+ int int_val;
+ long long_val;
+ float float_val;
+ double double_val;
+ char * string_val;
+ } _local_val;
+
+ vector <SGPropertyChangeListener *> * _listeners;
+
+
+\f
+ /**
+ * A very simple hash table with no remove functionality.
+ */
+ class hash_table {
+ public:
+
+ /**
+ * An entry in a bucket in a hash table.
+ */
+ class entry {
+ public:
+ entry ();
+ virtual ~entry ();
+ virtual const char * get_key () { return _key; }
+ virtual void set_key (const char * key);
+ virtual SGPropertyNode * get_value () { return _value; }
+ virtual void set_value (SGPropertyNode * value);
+ private:
+ char * _key;
+ SGPropertyNode * _value;
+ };
+
+
+ /**
+ * A bucket in a hash table.
+ */
+ class bucket {
+ public:
+ bucket ();
+ virtual ~bucket ();
+ virtual entry * get_entry (const char * key, bool create = false);
+ private:
+ int _length;
+ entry ** _entries;
+ };
+
+ friend class bucket;
+
+ hash_table ();
+ virtual ~hash_table ();
+ virtual SGPropertyNode * get (const char * key);
+ virtual void put (const char * key, SGPropertyNode * value);
+
+ private:
+ unsigned int hashcode (const char * key);
+ unsigned int _data_length;
+ bucket ** _data;
+ };
+
+};
+
+#endif // __PROPS_HXX
+
+// end of props.hxx
--- /dev/null
+
+#include <simgear/compiler.h>
+
+#include <stdlib.h> // atof() atoi()
+
+#include <simgear/sg_inlines.h>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/xml/easyxml.hxx>
+
+#include "props.hxx"
+#include "props_io.hxx"
+
+#include STL_IOSTREAM
+#include STL_FSTREAM
+#include STL_STRING
+#include <vector>
+#include <map>
+
+SG_USING_STD(istream);
+SG_USING_STD(ifstream);
+SG_USING_STD(ostream);
+SG_USING_STD(ofstream);
+SG_USING_STD(string);
+SG_USING_STD(vector);
+SG_USING_STD(map);
+
+#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list visitor, for XML parsing.
+////////////////////////////////////////////////////////////////////////
+
+class PropsVisitor : public XMLVisitor
+{
+public:
+
+ PropsVisitor (SGPropertyNode * root, const string &base)
+ : _root(root), _level(0), _base(base), _hasException(false) {}
+
+ virtual ~PropsVisitor () {}
+
+ void startXML ();
+ void endXML ();
+ 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 column);
+
+ bool hasException () const { return _hasException; }
+ sg_io_exception &getException () { return _exception; }
+ void setException (const sg_io_exception &exception) {
+ _exception = exception;
+ _hasException = true;
+ }
+
+private:
+
+ struct State
+ {
+ State () : node(0), type(""), mode(DEFAULT_MODE) {}
+ State (SGPropertyNode * _node, const char * _type, int _mode)
+ : node(_node), type(_type), mode(_mode) {}
+ SGPropertyNode * node;
+ string type;
+ int mode;
+ map<string,int> counters;
+ };
+
+ State &state () { return _state_stack[_state_stack.size() - 1]; }
+
+ void push_state (SGPropertyNode * node, const char * type, int mode) {
+ if (type == 0)
+ _state_stack.push_back(State(node, "unspecified", mode));
+ else
+ _state_stack.push_back(State(node, type, mode));
+ _level++;
+ _data = "";
+ }
+
+ void pop_state () {
+ _state_stack.pop_back();
+ _level--;
+ }
+
+ string _data;
+ SGPropertyNode * _root;
+ int _level;
+ vector<State> _state_stack;
+ string _base;
+ sg_io_exception _exception;
+ bool _hasException;
+};
+
+void
+PropsVisitor::startXML ()
+{
+ _level = 0;
+ _state_stack.resize(0);
+}
+
+void
+PropsVisitor::endXML ()
+{
+ _level = 0;
+ _state_stack.resize(0);
+}
+
+
+/**
+ * Check a yes/no flag, with default.
+ */
+static bool
+checkFlag (const char * flag, bool defaultState = true)
+{
+ if (flag == 0)
+ return defaultState;
+ else if (string(flag) == "y")
+ return true;
+ else if (string(flag) == "n")
+ return false;
+ else {
+ string message = "Unrecognized flag value '";
+ message += flag;
+ message += '\'';
+ // FIXME: add location info
+ throw sg_io_exception(message, "SimGear Property Reader");
+ }
+}
+
+void
+PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
+{
+ State &st = state();
+ const char * attval;
+
+ if (_level == 0) {
+ if (string(name) != (string)"PropertyList") {
+ string message = "Root element name is ";
+ message += name;
+ message += "; expected PropertyList";
+ throw sg_io_exception(message, "SimGear Property Reader");
+ }
+
+ // Check for an include.
+ attval = atts.getValue("include");
+ if (attval != 0) {
+ SGPath path(SGPath(_base).dir());
+ path.append(attval);
+ try {
+ readProperties(path.str(), _root);
+ } catch (sg_io_exception &e) {
+ setException(e);
+ }
+ }
+
+ push_state(_root, "", DEFAULT_MODE);
+ }
+
+ else {
+ // Get the index.
+ attval = atts.getValue("n");
+ int index = 0;
+ if (attval != 0) {
+ index = atoi(attval);
+ st.counters[name] = SG_MAX2(st.counters[name], index+1);
+ } else {
+ index = st.counters[name];
+ st.counters[name]++;
+ }
+
+ // Got the index, so grab the node.
+ SGPropertyNode * node = st.node->getChild(name, index, true);
+
+ // Get the access-mode attributes,
+ // but don't set yet (in case they
+ // prevent us from recording the value).
+ int mode = 0;
+
+ attval = atts.getValue("read");
+ if (checkFlag(attval, true))
+ mode |= SGPropertyNode::READ;
+ attval = atts.getValue("write");
+ if (checkFlag(attval, true))
+ mode |= SGPropertyNode::WRITE;
+ attval = atts.getValue("archive");
+ if (checkFlag(attval, false))
+ mode |= SGPropertyNode::ARCHIVE;
+ attval = atts.getValue("trace-read");
+ if (checkFlag(attval, false))
+ mode |= SGPropertyNode::TRACE_READ;
+ attval = atts.getValue("trace-write");
+ if (checkFlag(attval, false))
+ mode |= SGPropertyNode::TRACE_WRITE;
+
+ // Check for an alias.
+ attval = atts.getValue("alias");
+ if (attval != 0) {
+ if (!node->alias(attval))
+ SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
+ }
+
+ // Check for an include.
+ attval = atts.getValue("include");
+ if (attval != 0) {
+ SGPath path(SGPath(_base).dir());
+ path.append(attval);
+ try {
+ readProperties(path.str(), node);
+ } catch (sg_io_exception &e) {
+ setException(e);
+ }
+ }
+
+ push_state(node, atts.getValue("type"), mode);
+ }
+}
+
+void
+PropsVisitor::endElement (const char * name)
+{
+ State &st = state();
+ bool ret;
+
+ // 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);
+ else
+ ret = st.node->setBoolValue(false);
+ } else if (st.type == "int") {
+ ret = st.node->setIntValue(atoi(_data.c_str()));
+ } else if (st.type == "long") {
+ ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
+ } else if (st.type == "float") {
+ ret = st.node->setFloatValue(atof(_data.c_str()));
+ } else if (st.type == "double") {
+ ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
+ } else if (st.type == "string") {
+ ret = st.node->setStringValue(_data.c_str());
+ } else if (st.type == "unspecified") {
+ ret = st.node->setUnspecifiedValue(_data.c_str());
+ } else {
+ string message = "Unrecognized data type '";
+ message += st.type;
+ message += '\'';
+ // FIXME: add location information
+ throw sg_io_exception(message, "SimGear Property Reader");
+ }
+ if (!ret)
+ SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
+ << st.node->getPath() << " to value \""
+ << _data << "\" with type " << st.type);
+ }
+
+ // Set the access-mode attributes now,
+ // once the value has already been
+ // assigned.
+ st.node->setAttributes(st.mode);
+
+ pop_state();
+}
+
+void
+PropsVisitor::data (const char * s, int length)
+{
+ if (state().node->nChildren() == 0)
+ _data.append(string(s, length));
+}
+
+void
+PropsVisitor::warning (const char * message, int line, int column)
+{
+ SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
+ << message << " at line " << line << ", column " << column);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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.
+ */
+void
+readProperties (istream &input, SGPropertyNode * start_node,
+ const string &base)
+{
+ PropsVisitor visitor(start_node, base);
+ readXML(input, visitor, base);
+ if (visitor.hasException())
+ throw visitor.getException();
+}
+
+
+/**
+ * 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.
+ */
+void
+readProperties (const string &file, SGPropertyNode * start_node)
+{
+ PropsVisitor visitor(start_node, file);
+ readXML(file, visitor);
+ if (visitor.hasException())
+ throw visitor.getException();
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list writer.
+////////////////////////////////////////////////////////////////////////
+
+#define INDENT_STEP 2
+
+/**
+ * Return the type name.
+ */
+static const char *
+getTypeName (SGPropertyNode::Type type)
+{
+ switch (type) {
+ case SGPropertyNode::UNSPECIFIED:
+ return "unspecified";
+ case SGPropertyNode::BOOL:
+ return "bool";
+ case SGPropertyNode::INT:
+ return "int";
+ case SGPropertyNode::LONG:
+ return "long";
+ case SGPropertyNode::FLOAT:
+ return "float";
+ case SGPropertyNode::DOUBLE:
+ return "double";
+ case SGPropertyNode::STRING:
+ return "string";
+ case SGPropertyNode::ALIAS:
+ case SGPropertyNode::NONE:
+ return "unspecified";
+ }
+
+ // keep the compiler from squawking
+ return "unspecified";
+}
+
+
+/**
+ * Escape characters for output.
+ */
+static void
+writeData (ostream &output, const string &data)
+{
+ for (int i = 0; i < (int)data.size(); i++) {
+ switch (data[i]) {
+ case '&':
+ output << "&";
+ break;
+ case '<':
+ output << "<";
+ break;
+ case '>':
+ output << ">";
+ break;
+ default:
+ output << data[i];
+ break;
+ }
+ }
+}
+
+static void
+doIndent (ostream &output, int indent)
+{
+ while (indent-- > 0) {
+ output << ' ';
+ }
+}
+
+
+static void
+writeAtts (ostream &output, const SGPropertyNode * node)
+{
+ int index = node->getIndex();
+
+ if (index != 0)
+ output << " n=\"" << index << '"';
+
+#if 0
+ if (!node->getAttribute(SGPropertyNode::READ))
+ output << " read=\"n\"";
+
+ if (!node->getAttribute(SGPropertyNode::WRITE))
+ output << " write=\"n\"";
+
+ if (node->getAttribute(SGPropertyNode::ARCHIVE))
+ output << " archive=\"y\"";
+#endif
+
+}
+
+
+/**
+ * Test whether a node is archivable or has archivable descendants.
+ */
+static bool
+isArchivable (const SGPropertyNode * node)
+{
+ // FIXME: it's inefficient to do this all the time
+ if (node->getAttribute(SGPropertyNode::ARCHIVE))
+ return true;
+ else {
+ int nChildren = node->nChildren();
+ for (int i = 0; i < nChildren; i++)
+ if (isArchivable(node->getChild(i)))
+ return true;
+ }
+ return false;
+}
+
+
+static bool
+writeNode (ostream &output, const SGPropertyNode * node,
+ bool write_all, int indent)
+{
+ // Don't write the node or any of
+ // its descendants unless it is
+ // allowed to be archived.
+ if (!write_all && !isArchivable(node))
+ return true; // Everything's OK, but we won't write.
+
+ const string name = node->getName();
+ int nChildren = node->nChildren();
+
+ // If there is a literal value,
+ // write it first.
+ if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) {
+ doIndent(output, indent);
+ output << '<' << name;
+ writeAtts(output, node);
+ if (node->isAlias() && node->getAliasTarget() != 0) {
+ output << " alias=\"" << node->getAliasTarget()->getPath()
+ << "\"/>" << endl;
+ } else {
+ if (node->getType() != SGPropertyNode::UNSPECIFIED)
+ output << " type=\"" << getTypeName(node->getType()) << '"';
+ output << '>';
+ writeData(output, node->getStringValue());
+ output << "</" << name << '>' << endl;
+ }
+ }
+
+ // If there are children, write them next.
+ if (nChildren > 0 || node->isAlias()) {
+ doIndent(output, indent);
+ output << '<' << name;
+ writeAtts(output, node);
+ output << '>' << endl;
+ for (int i = 0; i < nChildren; i++)
+ writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP);
+ doIndent(output, indent);
+ output << "</" << name << '>' << endl;
+ }
+
+ return true;
+}
+
+
+void
+writeProperties (ostream &output, const SGPropertyNode * start_node,
+ bool write_all)
+{
+ int nChildren = start_node->nChildren();
+
+ output << "<?xml version=\"1.0\"?>" << endl << endl;
+ output << "<PropertyList>" << endl;
+
+ for (int i = 0; i < nChildren; i++) {
+ writeNode(output, start_node->getChild(i), write_all, INDENT_STEP);
+ }
+
+ output << "</PropertyList>" << endl;
+}
+
+
+void
+writeProperties (const string &file, const SGPropertyNode * start_node,
+ bool write_all)
+{
+ ofstream output(file.c_str());
+ if (output.good()) {
+ writeProperties(output, start_node, write_all);
+ } else {
+ throw sg_io_exception("Cannot open file", sg_location(file));
+ }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Copy properties from one tree 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)
+{
+ bool retval = true;
+
+ // First, copy the actual value,
+ // if any.
+ if (in->hasValue()) {
+ switch (in->getType()) {
+ case SGPropertyNode::BOOL:
+ if (!out->setBoolValue(in->getBoolValue()))
+ retval = false;
+ break;
+ case SGPropertyNode::INT:
+ if (!out->setIntValue(in->getIntValue()))
+ retval = false;
+ break;
+ case SGPropertyNode::LONG:
+ if (!out->setLongValue(in->getLongValue()))
+ retval = false;
+ break;
+ case SGPropertyNode::FLOAT:
+ if (!out->setFloatValue(in->getFloatValue()))
+ retval = false;
+ break;
+ case SGPropertyNode::DOUBLE:
+ if (!out->setDoubleValue(in->getDoubleValue()))
+ retval = false;
+ break;
+ case SGPropertyNode::STRING:
+ if (!out->setStringValue(in->getStringValue()))
+ retval = false;
+ break;
+ case SGPropertyNode::UNSPECIFIED:
+ if (!out->setUnspecifiedValue(in->getStringValue()))
+ retval = false;
+ break;
+ default:
+ string message = "Unknown internal SGPropertyNode type";
+ message += in->getType();
+ throw sg_error(message, "SimGear Property Reader");
+ }
+ }
+
+ // Next, copy the children.
+ int nChildren = in->nChildren();
+ for (int i = 0; i < nChildren; i++) {
+ const SGPropertyNode * in_child = in->getChild(i);
+ SGPropertyNode * out_child = out->getChild(in_child->getName(),
+ in_child->getIndex(),
+ true);
+ if (!copyProperties(in_child, out_child))
+ retval = false;
+ }
+
+ return retval;
+}
+
+// end of props_io.cxx
--- /dev/null
+/**
+ * \file props_io.hxx
+ * Interface definition for property list io.
+ * Started Fall 2000 by David Megginson, david@megginson.com
+ * This code is released into the Public Domain.
+ *
+ * See props.html for documentation [replace with URL when available].
+ *
+ * $Id$
+ */
+
+#ifndef __PROPS_IO_HXX
+#define __PROPS_IO_HXX
+
+#include <simgear/compiler.h>
+#include <simgear/props/props.hxx>
+
+#include <stdio.h>
+
+#include STL_STRING
+#include <vector>
+#include <map>
+#include STL_IOSTREAM
+
+SG_USING_STD(string);
+SG_USING_STD(vector);
+SG_USING_STD(map);
+SG_USING_STD(istream);
+SG_USING_STD(ostream);
+
+/**
+ * Read properties from an XML input stream.
+ */
+void readProperties (istream &input, SGPropertyNode * start_node,
+ const string &base = "");
+
+
+/**
+ * Read properties from an XML file.
+ */
+void readProperties (const string &file, SGPropertyNode * start_node);
+
+
+/**
+ * Write properties to an XML output stream.
+ */
+void writeProperties (ostream &output, const SGPropertyNode * start_node,
+ bool write_all = false);
+
+
+/**
+ * Write properties to an XML file.
+ */
+void writeProperties (const string &file, const SGPropertyNode * start_node,
+ bool write_all = false);
+
+
+/**
+ * Copy properties from one node to another.
+ */
+bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out);
+
+
+#endif // __PROPS_IO_HXX
+
+// end of props_io.hxx
--- /dev/null
+
+////////////////////////////////////////////////////////////////////////
+// Test harness.
+////////////////////////////////////////////////////////////////////////
+
+#include <simgear/compiler.h>
+
+#include STL_IOSTREAM
+#include "props.hxx"
+#include "props_io.hxx"
+
+SG_USING_STD(cout);
+SG_USING_STD(cerr);
+SG_USING_STD(endl);
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Sample object.
+////////////////////////////////////////////////////////////////////////
+
+class Stuff {
+public:
+ Stuff () : _stuff(199.0) {}
+ virtual float getStuff () const { return _stuff; }
+ virtual void setStuff (float stuff) { _stuff = stuff; }
+ virtual float getStuff (int index) const { return _stuff * index; }
+ virtual void setStuff (int index, float val) {
+ _stuff = val / index;
+ }
+private:
+ float _stuff;
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Sample function.
+////////////////////////////////////////////////////////////////////////
+
+static int get100 () { return 100; }
+
+static double getNum (int index) { return 1.0 / index; }
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Show a value.
+////////////////////////////////////////////////////////////////////////
+
+static void
+show_values (const SGPropertyNode * node)
+{
+ cout << "Bool: " << (node->getBoolValue() ? "true" : "false") << endl;
+ cout << "Int: " << node->getIntValue() << endl;
+ cout << "Float: " << node->getFloatValue() << endl;
+ cout << "Double: " << node->getDoubleValue() << endl;
+ cout << "String: " << node->getStringValue() << endl;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Test individual values.
+////////////////////////////////////////////////////////////////////////
+
+static void
+test_value ()
+{
+ SGPropertyNode * node;
+
+ cout << endl << "Value" << endl << endl;
+
+ //
+ // Test coercion for getters.
+ //
+
+ cout << "Testing coercion from bool (expect true)" << endl;
+ node = new SGPropertyNode;
+ node->setBoolValue(true);
+ show_values(node);
+ delete node;
+ cout << endl;
+
+ cout << "Testing coercion from int (expect 128)" << endl;
+ node = new SGPropertyNode;
+ node->setIntValue(128);
+ show_values(node);
+ delete node;
+ cout << endl;
+
+ cout << "Testing coercion from float (expect 1.0/3.0)" << endl;
+ node = new SGPropertyNode;
+ node->setFloatValue(1.0/3.0);
+ show_values(node);
+ delete node;
+ cout << endl;
+
+ cout << "Testing coercion from double (expect 1.0/3.0)" << endl;
+ node = new SGPropertyNode;
+ node->setDoubleValue(1.0/3.0);
+ show_values(node);
+ delete node;
+ cout << endl;
+
+ cout << "Testing coercion from string (expect 10e4)" << endl;
+ node = new SGPropertyNode;
+ node->setStringValue("10e4");
+ show_values(node);
+ delete node;
+ cout << endl;
+
+ cout << "Testing coercion from unspecified (expect -10e-4)" << endl;
+ node = new SGPropertyNode;
+ node->setUnspecifiedValue("-10e-4");
+ show_values(node);
+ delete node;
+ cout << endl;
+
+ //
+ // Test coercion for setters.
+ //
+
+ node = new SGPropertyNode;
+
+ cout << "Testing coercion to bool from bool (expect false)" << endl;
+ node->setBoolValue(false);
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing coercion to bool from int (expect 1)" << endl;
+ node->setIntValue(1);
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing coercion to bool from float (expect 1.1)" << endl;
+ node->setFloatValue(1.1);
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing coercion to bool from double (expect 1.1)" << endl;
+ node->setDoubleValue(1.1);
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing coercion to bool from string (expect 1e10)" << endl;
+ node->setStringValue("1e10");
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing coercion to bool from unspecified (expect 1e10)" << endl;
+ node->setUnspecifiedValue("1e10");
+ show_values(node);
+ cout << endl;
+
+ // Test tying to a pointer.
+
+ static int myValue = 10;
+
+ cout << "Testing tying to a pointer (expect 10)" << endl;
+ if (!node->tie(SGRawValuePointer<int>(&myValue), false))
+ cout << "*** FAILED TO TIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Changing base variable (expect -5)" << endl;
+ myValue = -5;
+ show_values(node);
+ if (!node->untie())
+ cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+ cout << endl;
+
+
+ // Test tying to static functions.
+
+ cout << "Create a new int value (expect 10)" << endl;
+ node->setIntValue(10);
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing tying to static getter (expect 100)" << endl;
+ if (!node->tie(SGRawValueFunctions<int>(get100)))
+ cout << "*** FAILED TO TIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Try changing value with no setter (expect 100)" << endl;
+ if (node->setIntValue(200))
+ cout << "*** setIntValue did not return false!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Untie value (expect 100)" << endl;
+ if (!node->untie())
+ cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Try changing value (expect 200)" << endl;
+ if (!node->setIntValue(200))
+ cout << "*** setIntValue RETURNED FALSE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ // Test tying to indexed static functions.
+
+ cout << "Create a new int value (expect 10)" << endl;
+ node->setIntValue(10);
+ show_values(node);
+ cout << endl;
+
+ cout << "Testing tying to indexed static getter (0.3333...)" << endl;
+ if (!node->tie(SGRawValueFunctionsIndexed<double>(3, getNum)))
+ cout << "*** FAILED TO TIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Untie value (expect 0.3333...)" << endl;
+ if (!node->untie())
+ cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+
+ // Test methods.
+
+ cout << "Try tying to an object without defaults (expect 199)" << endl;
+ Stuff stuff;
+ SGRawValueMethods<class Stuff,float> tiedstuff(stuff,
+ &Stuff::getStuff,
+ &Stuff::setStuff);
+ if (!node->tie(tiedstuff, false))
+ cout << "*** FAILED TO TIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Try untying from object (expect 199)" << endl;
+ if (!node->untie())
+ cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Try tying to an indexed method (expect 199)" << endl;
+ if (!node->tie(SGRawValueMethodsIndexed<class Stuff, float>
+ (stuff, 2, &Stuff::getStuff, &Stuff::setStuff)))
+ cout << "*** FAILED TO TIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ node->untie();
+
+ cout << "Change value (expect 50)" << endl;
+ if (!node->setIntValue(50))
+ cout << "*** FAILED TO SET VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ cout << "Try tying to an object with defaults (expect 50)" << endl;
+ if (!node->tie(tiedstuff, true))
+ cout << "*** FAILED TO TIE VALUE!!!" << endl;
+ show_values(node);
+ cout << endl;
+
+ delete node;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Check property nodes.
+////////////////////////////////////////////////////////////////////////
+
+static void
+dump_node (const SGPropertyNode * node)
+{
+ writeProperties(cout, node, true);
+}
+
+static void
+test_property_nodes ()
+{
+ SGPropertyNode root;
+ cout << "Created root node " << root.getPath() << endl;
+
+ SGPropertyNode * child = root.getChild("foo", 0, true);
+
+ SGPropertyNode *grandchild = child->getChild("bar", 0, true);
+ grandchild->setDoubleValue(100);
+ grandchild = child->getChild("bar", 1, true);
+ grandchild->setDoubleValue(200);
+ grandchild = child->getChild("bar", 2, true);
+ grandchild->setDoubleValue(300);
+ grandchild = child->getChild("bar", 3, true);
+ grandchild->setDoubleValue(400);
+
+ child = root.getChild("hack", 0, true);
+
+ grandchild = child->getChild("bar", 0, true);
+ grandchild->setDoubleValue(100);
+ grandchild = child->getChild("bar", 3, true);
+ grandchild->setDoubleValue(200);
+ grandchild = child->getChild("bar", 1, true);
+ grandchild->setDoubleValue(300);
+ grandchild = child->getChild("bar", 2, true);
+ grandchild->setDoubleValue(400);
+ dump_node(&root);
+
+ cout << "Trying path (expect /foo[0]/bar[0])" << endl;
+ grandchild = root.getNode("/hack/../foo/./bar[0]");
+ cout << "Path is " << grandchild->getPath() << endl;
+ cout << endl;
+
+ cout << "Looking for all /hack[0]/bar children" << endl;
+ vector<SGPropertyNode_ptr> bar = child->getChildren("bar");
+ cout << "There are " << bar.size() << " matches" << endl;
+ for (int i = 0; i < (int)bar.size(); i++)
+ cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl;
+ cout << endl;
+
+ cout << "Testing addition of a totally empty node" << endl;
+ if (root.getNode("/a/b/c", true) == 0)
+ cerr << "** failed to create /a/b/c" << endl;
+ dump_node(&root);
+ cout << endl;
+}
+
+
+int main (int ac, char ** av)
+{
+ test_value();
+ test_property_nodes();
+
+ for (int i = 1; i < ac; i++) {
+ try {
+ cout << "Reading " << av[i] << endl;
+ SGPropertyNode root;
+ readProperties(av[i], &root);
+ writeProperties(cout, &root, true);
+ cout << endl;
+ } catch (string &message) {
+ cout << "Aborted with " << message << endl;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+includedir = @includedir@/scene
+
+SUBDIRS = model
+
+# lib_LIBRARIES = libsgscene.a
+
+# noinst_HEADERS =
+
+# include_HEADERS =
+
+# libsgscene_a_SOURCES =
+
+INCLUDES = -I$(top_srcdir)
--- /dev/null
+includedir = @includedir@/scene/model
+
+lib_LIBRARIES = libsgmodel.a
+
+noinst_HEADERS =
+
+include_HEADERS = location.hxx
+
+libsgmodel_a_SOURCES = location.cxx
+
+INCLUDES = -I$(top_srcdir)
--- /dev/null
+// location.cxx -- class for determining model location in the flightgear world.
+//
+// Written by Jim Wilson, David Megginson, started April 2002.
+// Based largely on code by Curtis Olson and Norman Vine.
+//
+// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+
+#include <simgear/compiler.h>
+
+#ifdef HAVE_CONFIG_H
+# include <simgear_config.h>
+#endif
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/constants.h>
+#include <simgear/math/point3d.hxx>
+#include <simgear/math/polar3d.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/vector.hxx>
+
+// #include <Scenery/scenery.hxx>
+// #include "globals.hxx"
+
+#include "location.hxx"
+
+
+/**
+ * make model transformation Matrix - based on optimizations by NHV
+ */
+static void MakeTRANS( sgMat4 dst, const double Theta,
+ const double Phi, const double Psi,
+ const sgMat4 UP)
+{
+ SGfloat cosTheta = (SGfloat) cos(Theta);
+ SGfloat sinTheta = (SGfloat) sin(Theta);
+ SGfloat cosPhi = (SGfloat) cos(Phi);
+ SGfloat sinPhi = (SGfloat) sin(Phi);
+ SGfloat sinPsi = (SGfloat) sin(Psi) ;
+ SGfloat cosPsi = (SGfloat) cos(Psi) ;
+
+ sgMat4 tmp;
+
+ tmp[0][0] = cosPhi * cosTheta;
+ tmp[0][1] = sinPhi * cosPsi + cosPhi * -sinTheta * -sinPsi;
+ tmp[0][2] = sinPhi * sinPsi + cosPhi * -sinTheta * cosPsi;
+
+ tmp[1][0] = -sinPhi * cosTheta;
+ tmp[1][1] = cosPhi * cosPsi + -sinPhi * -sinTheta * -sinPsi;
+ tmp[1][2] = cosPhi * sinPsi + -sinPhi * -sinTheta * cosPsi;
+
+ tmp[2][0] = sinTheta;
+ tmp[2][1] = cosTheta * -sinPsi;
+ tmp[2][2] = cosTheta * cosPsi;
+
+ float a = UP[0][0];
+ float b = UP[1][0];
+ float c = UP[2][0];
+ dst[2][0] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
+ dst[1][0] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
+ dst[0][0] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
+ dst[3][0] = SG_ZERO ;
+
+ a = UP[0][1];
+ b = UP[1][1];
+ c = UP[2][1];
+ dst[2][1] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
+ dst[1][1] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
+ dst[0][1] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
+ dst[3][1] = SG_ZERO ;
+
+ a = UP[0][2];
+ c = UP[2][2];
+ dst[2][2] = a*tmp[0][0] + c*tmp[0][2] ;
+ dst[1][2] = a*tmp[1][0] + c*tmp[1][2] ;
+ dst[0][2] = -(a*tmp[2][0] + c*tmp[2][2]) ;
+ dst[3][2] = SG_ZERO ;
+
+ dst[2][3] = SG_ZERO ;
+ dst[1][3] = SG_ZERO ;
+ dst[0][3] = SG_ZERO ;
+ dst[3][3] = SG_ONE ;
+
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGLocation.
+////////////////////////////////////////////////////////////////////////
+
+// Constructor
+FGLocation::FGLocation( void ):
+ _dirty(true),
+ _lon_deg(0),
+ _lat_deg(0),
+ _alt_ft(0),
+ _roll_deg(0),
+ _pitch_deg(0),
+ _heading_deg(0),
+ _cur_elev_m(0),
+ _tile_center(0)
+{
+ sgdZeroVec3(_absolute_view_pos);
+ sgZeroVec3(_relative_view_pos);
+ sgZeroVec3(_zero_elev_view_pos);
+ sgMakeRotMat4( UP, 0.0, 0.0, 0.0 );
+ sgMakeRotMat4( TRANS, 0.0, 0.0, 0.0 );
+}
+
+
+// Destructor
+FGLocation::~FGLocation( void ) {
+}
+
+void
+FGLocation::init ()
+{
+}
+
+void
+FGLocation::bind ()
+{
+}
+
+void
+FGLocation::unbind ()
+{
+}
+
+void
+FGLocation::setPosition (double lon_deg, double lat_deg, double alt_ft)
+{
+ _dirty = true;
+ _lon_deg = lon_deg;
+ _lat_deg = lat_deg;
+ _alt_ft = alt_ft;
+}
+
+void
+FGLocation::setOrientation (double roll_deg, double pitch_deg, double heading_deg)
+{
+ _dirty = true;
+ _roll_deg = roll_deg;
+ _pitch_deg = pitch_deg;
+ _heading_deg = heading_deg;
+}
+
+double *
+FGLocation::get_absolute_view_pos( const Point3D scenery_center )
+{
+ if ( _dirty ) {
+ recalc( scenery_center );
+ }
+ return _absolute_view_pos;
+}
+
+float *
+FGLocation::getRelativeViewPos( const Point3D scenery_center )
+{
+ if ( _dirty ) {
+ recalc( scenery_center );
+ }
+ return _relative_view_pos;
+}
+
+float *
+FGLocation::getZeroElevViewPos( const Point3D scenery_center )
+{
+ if ( _dirty ) {
+ recalc( scenery_center );
+ }
+ return _zero_elev_view_pos;
+}
+
+
+// recalc() is done every time one of the setters is called (making the
+// cached data "dirty") on the next "get". It calculates all the outputs
+// for viewer.
+void
+FGLocation::recalc( const Point3D scenery_center )
+{
+
+ recalcPosition( _lon_deg, _lat_deg, _alt_ft, scenery_center );
+
+ // Make the world up rotation matrix for eye positioin...
+ sgMakeRotMat4( UP, _lon_deg, 0.0, -_lat_deg );
+
+
+ // get the world up radial vector from planet center for output
+ sgSetVec3( _world_up, UP[0][0], UP[0][1], UP[0][2] );
+
+ // Creat local matrix with current geodetic position. Converting
+ // the orientation (pitch/roll/heading) to vectors.
+ MakeTRANS( TRANS, _pitch_deg * SG_DEGREES_TO_RADIANS,
+ _roll_deg * SG_DEGREES_TO_RADIANS,
+ -_heading_deg * SG_DEGREES_TO_RADIANS,
+ UP);
+
+ // Given a vector pointing straight down (-Z), map into onto the
+ // local plane representing "horizontal". This should give us the
+ // local direction for moving "south".
+ sgVec3 minus_z;
+ sgSetVec3( minus_z, 0.0, 0.0, -1.0 );
+
+ sgmap_vec_onto_cur_surface_plane(_world_up, _relative_view_pos, minus_z,
+ _surface_south);
+ sgNormalizeVec3(_surface_south);
+
+ // now calculate the surface east vector
+ sgVec3 world_down;
+ sgNegateVec3(world_down, _world_up);
+ sgVectorProductVec3(_surface_east, _surface_south, world_down);
+
+ set_clean();
+}
+
+void
+FGLocation::recalcPosition( double lon_deg, double lat_deg, double alt_ft,
+ const Point3D scenery_center ) const
+{
+ double sea_level_radius_m;
+ double lat_geoc_rad;
+
+
+ // Convert from geodetic to geocentric
+ // coordinates.
+ sgGeodToGeoc(lat_deg * SGD_DEGREES_TO_RADIANS,
+ alt_ft * SG_FEET_TO_METER,
+ &sea_level_radius_m,
+ &lat_geoc_rad);
+
+ // Calculate the cartesian coordinates
+ // of point directly below at sea level.
+ // aka Zero Elevation Position
+ Point3D p = Point3D(lon_deg * SG_DEGREES_TO_RADIANS,
+ lat_geoc_rad,
+ sea_level_radius_m);
+ Point3D tmp = sgPolarToCart3d(p) - _tile_center;
+ sgSetVec3(_zero_elev_view_pos, tmp[0], tmp[1], tmp[2]);
+
+ // Calculate the absolute view position
+ // in fgfs coordinates.
+ // aka Absolute View Position
+ p.setz(p.radius() + alt_ft * SG_FEET_TO_METER);
+ tmp = sgPolarToCart3d(p);
+ sgdSetVec3(_absolute_view_pos, tmp[0], tmp[1], tmp[2]);
+
+ // Calculate the relative view position
+ // from the scenery center.
+ // aka Relative View Position
+
+ // FIXME: view position should ONLY be calculated in the viewer...
+ // Anything else should calculate their own positions relative to the
+ // viewer's tile_center.
+ sgdVec3 center;
+ sgdSetVec3( center,
+ scenery_center.x(), scenery_center.y(), scenery_center.z() );
+ sgdVec3 view_pos;
+ sgdSubVec3(view_pos, _absolute_view_pos, center);
+ sgSetVec3(_relative_view_pos, view_pos);
+
+}
+
+void
+FGLocation::update (int dt)
+{
+}
--- /dev/null
+// location.hxx -- class for determining model location in the flightgear world.
+//
+// Written by Jim Wilson, David Megginson, started April 2002.
+//
+// Copyright (C) 2002 Jim Wilson, David Megginson
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+
+#ifndef _LOCATION_HXX
+#define _LOCATION_HXX
+
+
+#ifndef __cplusplus
+# error This library requires C++
+#endif
+
+#include <simgear/compiler.h>
+#include <simgear/constants.h>
+#include <simgear/math/point3d.hxx>
+
+#include <plib/sg.h> // plib include
+
+// #include "fgfs.hxx"
+
+
+// Define a structure containing view information
+class FGLocation
+{
+
+public:
+
+ // Constructor
+ FGLocation( void );
+
+ // Destructor
+ virtual ~FGLocation( void );
+
+ //////////////////////////////////////////////////////////////////////
+ // Part 1: standard FGSubsystem implementation.
+ //////////////////////////////////////////////////////////////////////
+
+ virtual void init ();
+ virtual void bind ();
+ virtual void unbind ();
+ void update (int dt);
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Part 2: user settings.
+ //////////////////////////////////////////////////////////////////////
+
+ // Geodetic position of model...
+ virtual double getLongitude_deg () const { return _lon_deg; }
+ virtual double getLatitude_deg () const { return _lat_deg; }
+ virtual double getAltitudeASL_ft () const { return _alt_ft; }
+ virtual void setPosition (double lon_deg, double lat_deg, double alt_ft);
+
+
+ // Reference orientation rotations...
+ // These are rotations that represent the plane attitude effect on
+ // the view (in Pilot view). IE The view frustrum rotates as the plane
+ // turns, pitches, and rolls.
+ // In model view (lookat/chaseview) these end up changing the angle that
+ // the eye is looking at the ojbect (ie the model).
+ // FIXME: the FGModel class should have its own version of these so that
+ // it can generate it's own model rotations.
+ virtual double getRoll_deg () const { return _roll_deg; }
+ virtual double getPitch_deg () const {return _pitch_deg; }
+ virtual double getHeading_deg () const {return _heading_deg; }
+ virtual void setOrientation (double roll_deg, double pitch_deg, double heading_deg);
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Part 3: output vectors and matrices in FlightGear coordinates.
+ //////////////////////////////////////////////////////////////////////
+
+ // Vectors and positions...
+
+ // Get zero view_pos
+ virtual float * get_view_pos() { return _relative_view_pos; }
+ // Get the absolute view position in fgfs coordinates.
+ virtual double * get_absolute_view_pos( const Point3D scenery_center );
+ // Get zero elev
+ virtual float * get_zero_elev() { return _zero_elev_view_pos; }
+ // Get world up vector
+ virtual float *get_world_up() { return _world_up; }
+ // Get the relative (to scenery center) view position in fgfs coordinates.
+ virtual float * getRelativeViewPos( const Point3D scenery_center );
+ // Get the absolute zero-elevation view position in fgfs coordinates.
+ virtual float * getZeroElevViewPos( const Point3D scenery_center );
+ // Get surface east vector
+ virtual float *get_surface_east() { return _surface_east; }
+ // Get surface south vector
+ virtual float *get_surface_south() { return _surface_south; }
+ // Elevation of ground under location (based on scenery output)...
+ void set_cur_elev_m ( double elev ) { _cur_elev_m = elev; }
+ inline double get_cur_elev_m () { return _cur_elev_m; }
+ // Interface to current buckets for use with tilemgr...
+ void set_tile_center ( Point3D tile_center ) { _tile_center = tile_center; }
+ inline Point3D get_tile_center () { return _tile_center; }
+
+ // Matrices...
+ virtual const sgVec4 *getTransformMatrix( const Point3D scenery_center ) {
+ if ( _dirty ) {
+ recalc( scenery_center );
+ }
+ return TRANS;
+ }
+ virtual const sgVec4 *getCachedTransformMatrix() { return TRANS; }
+ virtual const sgVec4 *getUpMatrix( const Point3D scenery_center ) {
+ if ( _dirty ) {
+ recalc( scenery_center );
+ }
+ return UP;
+ }
+ virtual const sgVec4 *getCachedUpMatrix() { return UP; }
+
+
+private:
+
+ //////////////////////////////////////////////////////////////////
+ // private data //
+ //////////////////////////////////////////////////////////////////
+
+ // flag forcing a recalc of derived view parameters
+ bool _dirty;
+
+ mutable sgdVec3 _absolute_view_pos;
+ mutable sgVec3 _relative_view_pos;
+ mutable sgVec3 _zero_elev_view_pos;
+
+ double _lon_deg;
+ double _lat_deg;
+ double _alt_ft;
+
+ double _roll_deg;
+ double _pitch_deg;
+ double _heading_deg;
+
+ // elevation of ground under this location...
+ double _cur_elev_m;
+ Point3D _tile_center;
+
+ // surface vector heading south
+ sgVec3 _surface_south;
+
+ // surface vector heading east (used to unambiguously align sky
+ // with sun)
+ sgVec3 _surface_east;
+
+ // world up vector (normal to the plane tangent to the earth's
+ // surface at the spot we are directly above
+ sgVec3 _world_up;
+
+ // sg versions of our friendly matrices
+ sgMat4 TRANS, UP;
+
+ //////////////////////////////////////////////////////////////////
+ // private functions //
+ //////////////////////////////////////////////////////////////////
+
+ void recalc( const Point3D scenery_center );
+ void recalcPosition( double lon_deg, double lat_deg, double alt_ft,
+ const Point3D scenery_center ) const;
+
+ inline void set_dirty() { _dirty = true; }
+ inline void set_clean() { _dirty = false; }
+
+};
+
+
+#endif // _LOCATION_HXX