Files
simgear/simgear/props/condition.cxx
T
Torsten Dreyer 09e610b900 Implement #327 Add condition checking the range of a value
New feature for <condition> elements:
An (optional) element <precision> allows for fuzzy equals checks
example:
<condition>
  <equals>
    <property>/foo</property>
    <value type="double">0.0</value>
    <precision>0.1</precision>
  </equals>
</condition>

This condition evaluates as true if /foo is within [-0.05..0.05]
(both inclusive)
The precision tag works for int, long, float and double propeties.
It has no meaning for bool properties. For string properties,
precision sets the length of the strings to compare.
2011-08-24 21:26:02 +02:00

624 lines
16 KiB
C++

// condition.cxx - 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_config.h>
#endif
// #include <iostream>
#include <simgear/structure/exception.hxx>
#include "props.hxx"
#include "condition.hxx"
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGExpression.hxx>
using std::istream;
using std::ostream;
/**
* Condition for a single property.
*
* This condition is true only if the property returns a boolean
* true value.
*/
class SGPropertyCondition : public SGCondition
{
public:
SGPropertyCondition ( SGPropertyNode *prop_root,
const char * propname );
virtual ~SGPropertyCondition ();
virtual bool test () const { return _node->getBoolValue(); }
private:
SGConstPropertyNode_ptr _node;
};
/**
* Condition with constant value
*
*/
class SGConstantCondition : public SGCondition
{
public:
SGConstantCondition (bool v) : _value(v) { ; }
virtual bool test () const { return _value; }
private:
bool _value;
};
/**
* Condition for a 'not' operator.
*
* This condition is true only if the child condition is false.
*/
class SGNotCondition : public SGCondition
{
public:
SGNotCondition (SGCondition * condition);
virtual ~SGNotCondition ();
virtual bool test () const;
private:
SGSharedPtr<SGCondition> _condition;
};
/**
* Condition for an 'and' group.
*
* This condition is true only if all of the conditions
* in the group are true.
*/
class SGAndCondition : public SGCondition
{
public:
SGAndCondition ();
virtual ~SGAndCondition ();
virtual bool test () const;
// transfer pointer ownership
virtual void addCondition (SGCondition * condition);
private:
std::vector<SGSharedPtr<SGCondition> > _conditions;
};
/**
* Condition for an 'or' group.
*
* This condition is true if at least one of the conditions in the
* group is true.
*/
class SGOrCondition : public SGCondition
{
public:
SGOrCondition ();
virtual ~SGOrCondition ();
virtual bool test () const;
// transfer pointer ownership
virtual void addCondition (SGCondition * condition);
private:
std::vector<SGSharedPtr<SGCondition> > _conditions;
};
/**
* Abstract base class for property comparison conditions.
*/
class SGComparisonCondition : public SGCondition
{
public:
enum Type {
LESS_THAN,
GREATER_THAN,
EQUALS
};
SGComparisonCondition (Type type, bool reverse = false);
virtual ~SGComparisonCondition ();
virtual bool test () const;
virtual void setLeftProperty( SGPropertyNode *prop_root,
const char * propname );
virtual void setRightProperty( SGPropertyNode *prop_root,
const char * propname );
virtual void setPrecisionProperty( SGPropertyNode *prop_root,
const char * propname );
// will make a local copy
virtual void setLeftValue (const SGPropertyNode * value);
virtual void setRightValue (const SGPropertyNode * value);
virtual void setPrecisionValue (const SGPropertyNode * value);
void setLeftDExpression(SGExpressiond* dexp);
void setRightDExpression(SGExpressiond* dexp);
void setPrecisionDExpression(SGExpressiond* dexp);
private:
Type _type;
bool _reverse;
SGPropertyNode_ptr _left_property;
SGPropertyNode_ptr _right_property;
SGPropertyNode_ptr _precision_property;
SGSharedPtr<SGExpressiond> _left_dexp;
SGSharedPtr<SGExpressiond> _right_dexp;
SGSharedPtr<SGExpressiond> _precision_dexp;
};
////////////////////////////////////////////////////////////////////////
// Implementation of SGCondition.
////////////////////////////////////////////////////////////////////////
SGCondition::SGCondition ()
{
}
SGCondition::~SGCondition ()
{
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGPropertyCondition.
////////////////////////////////////////////////////////////////////////
SGPropertyCondition::SGPropertyCondition ( SGPropertyNode *prop_root,
const char *propname )
: _node( prop_root->getNode(propname, true) )
{
}
SGPropertyCondition::~SGPropertyCondition ()
{
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGNotCondition.
////////////////////////////////////////////////////////////////////////
SGNotCondition::SGNotCondition (SGCondition * condition)
: _condition(condition)
{
}
SGNotCondition::~SGNotCondition ()
{
}
bool
SGNotCondition::test () const
{
return !(_condition->test());
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGAndCondition.
////////////////////////////////////////////////////////////////////////
SGAndCondition::SGAndCondition ()
{
}
SGAndCondition::~SGAndCondition ()
{
}
bool
SGAndCondition::test () const
{
int nConditions = _conditions.size();
for (int i = 0; i < nConditions; i++) {
if (!_conditions[i]->test())
return false;
}
return true;
}
void
SGAndCondition::addCondition (SGCondition * condition)
{
_conditions.push_back(condition);
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGOrCondition.
////////////////////////////////////////////////////////////////////////
SGOrCondition::SGOrCondition ()
{
}
SGOrCondition::~SGOrCondition ()
{
}
bool
SGOrCondition::test () const
{
int nConditions = _conditions.size();
for (int i = 0; i < nConditions; i++) {
if (_conditions[i]->test())
return true;
}
return false;
}
void
SGOrCondition::addCondition (SGCondition * condition)
{
_conditions.push_back(condition);
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGComparisonCondition.
////////////////////////////////////////////////////////////////////////
template<typename T>
static int doComp( T v1, T v2, T e )
{
T d = v1 - v2;
if( d < -e )
return SGComparisonCondition::LESS_THAN;
else if( d > e )
return SGComparisonCondition::GREATER_THAN;
else
return SGComparisonCondition::EQUALS;
}
static int
doComparison (const SGPropertyNode * left, const SGPropertyNode * right, const SGPropertyNode * precision )
{
using namespace simgear;
switch (left->getType()) {
case props::BOOL: {
bool v1 = left->getBoolValue();
bool v2 = right->getBoolValue();
if (v1 < v2)
return SGComparisonCondition::LESS_THAN;
else if (v1 > v2)
return SGComparisonCondition::GREATER_THAN;
else
return SGComparisonCondition::EQUALS;
break;
}
case props::INT:
return doComp<int>(left->getIntValue(), right->getIntValue(),
precision ? abs(precision->getIntValue()/2) : 0 );
case props::LONG:
return doComp<long>(left->getLongValue(), right->getLongValue(),
precision ? abs(precision->getLongValue()/2L) : 0L );
case props::FLOAT:
return doComp<float>(left->getFloatValue(), right->getFloatValue(),
precision ? fabs(precision->getFloatValue()/2.0f) : 0.0f );
case props::DOUBLE:
return doComp<double>(left->getDoubleValue(), right->getDoubleValue(),
precision ? fabs(precision->getDoubleValue()/2.0) : 0.0 );
case props::STRING:
case props::NONE:
case props::UNSPECIFIED: {
size_t l = precision ? precision->getLongValue() : string::npos;
string v1 = string(left->getStringValue()).substr(0,l);
string v2 = string(right->getStringValue()).substr(0,l);
if (v1 < v2)
return SGComparisonCondition::LESS_THAN;
else if (v1 > v2)
return SGComparisonCondition::GREATER_THAN;
else
return SGComparisonCondition::EQUALS;
break;
}
default:
throw sg_exception("condition: unrecognized node type in comparison");
}
return 0;
}
SGComparisonCondition::SGComparisonCondition (Type type, bool reverse)
: _type(type),
_reverse(reverse)
{
}
SGComparisonCondition::~SGComparisonCondition ()
{
}
bool
SGComparisonCondition::test () const
{
// Always fail if incompletely specified
if (!_left_property || !_right_property)
return false;
// Get LESS_THAN, EQUALS, or GREATER_THAN
if (_left_dexp) {
_left_property->setDoubleValue(_left_dexp->getValue(NULL));
}
if (_right_dexp) {
_right_property->setDoubleValue(_right_dexp->getValue(NULL));
}
if (_precision_dexp) {
_precision_property->setDoubleValue(_precision_dexp->getValue(NULL));
}
int cmp = doComparison(_left_property, _right_property, _precision_property );
if (!_reverse)
return (cmp == _type);
else
return (cmp != _type);
}
void
SGComparisonCondition::setLeftProperty( SGPropertyNode *prop_root,
const char * propname )
{
_left_property = prop_root->getNode(propname, true);
}
void
SGComparisonCondition::setRightProperty( SGPropertyNode *prop_root,
const char * propname )
{
_right_property = prop_root->getNode(propname, true);
}
void
SGComparisonCondition::setPrecisionProperty( SGPropertyNode *prop_root,
const char * propname )
{
_precision_property = prop_root->getNode(propname, true);
}
void
SGComparisonCondition::setLeftValue (const SGPropertyNode *node)
{
_left_property = new SGPropertyNode(*node);
}
void
SGComparisonCondition::setPrecisionValue (const SGPropertyNode *node)
{
_precision_property = new SGPropertyNode(*node);
}
void
SGComparisonCondition::setRightValue (const SGPropertyNode *node)
{
_right_property = new SGPropertyNode(*node);
}
void
SGComparisonCondition::setLeftDExpression(SGExpressiond* dexp)
{
_left_property = new SGPropertyNode();
_left_dexp = dexp;
}
void
SGComparisonCondition::setRightDExpression(SGExpressiond* dexp)
{
_right_property = new SGPropertyNode();
_right_dexp = dexp;
}
void
SGComparisonCondition::setPrecisionDExpression(SGExpressiond* dexp)
{
_precision_property = new SGPropertyNode();
_precision_dexp = dexp;
}
////////////////////////////////////////////////////////////////////////
// Read a condition and use it if necessary.
////////////////////////////////////////////////////////////////////////
// Forward declaration
static SGCondition * readCondition( SGPropertyNode *prop_root,
const SGPropertyNode *node );
static SGCondition *
readPropertyCondition( SGPropertyNode *prop_root,
const SGPropertyNode *node )
{
return new SGPropertyCondition( prop_root, node->getStringValue() );
}
static SGCondition *
readNotCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
int nChildren = node->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode * child = node->getChild(i);
SGCondition * condition = readCondition(prop_root, child);
if (condition != 0)
return new SGNotCondition(condition);
}
SG_LOG(SG_COCKPIT, SG_ALERT, "empty 'not' condition");
return 0;
}
static SGCondition *
readAndConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
SGAndCondition * andCondition = new SGAndCondition;
int nChildren = node->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode * child = node->getChild(i);
SGCondition * condition = readCondition(prop_root, child);
if (condition != 0)
andCondition->addCondition(condition);
}
return andCondition;
}
static SGCondition *
readOrConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
SGOrCondition * orCondition = new SGOrCondition;
int nChildren = node->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode * child = node->getChild(i);
SGCondition * condition = readCondition(prop_root, child);
if (condition != 0)
orCondition->addCondition(condition);
}
return orCondition;
}
static SGCondition *
readComparison( SGPropertyNode *prop_root,
const SGPropertyNode *node,
SGComparisonCondition::Type type,
bool reverse)
{
SGComparisonCondition * condition = new SGComparisonCondition(type, reverse);
if (node->nChildren() < 2 || node->nChildren() > 3 ) {
throw sg_exception("condition: comparison without two or three children");
}
const SGPropertyNode* left = node->getChild(0),
*right = node->getChild(1);
{
string leftName(left->getName());
if (leftName == "property") {
condition->setLeftProperty(prop_root, left->getStringValue());
} else if (leftName == "value") {
condition->setLeftValue(left);
} else if (leftName == "expression") {
SGExpressiond* exp = SGReadDoubleExpression(prop_root, left->getChild(0));
condition->setLeftDExpression(exp);
} else {
throw sg_exception("Unknown condition comparison left child:" + leftName);
}
}
{
string rightName(right->getName());
if (rightName == "property") {
condition->setRightProperty(prop_root, right->getStringValue());
} else if (rightName == "value") {
condition->setRightValue(right);
} else if (rightName == "expression") {
SGExpressiond* exp = SGReadDoubleExpression(prop_root, right->getChild(0));
condition->setRightDExpression(exp);
} else {
throw sg_exception("Unknown condition comparison right child:" + rightName);
}
}
if( node->nChildren() == 3 ) {
const SGPropertyNode *n = node->getChild(2);
string name(n->getName());
if (name == "precision-property") {
condition->setPrecisionProperty(prop_root, n->getStringValue());
} else if (name == "precision-value") {
condition->setPrecisionValue(n);
} else if (name == "precision-expression") {
SGExpressiond* exp = SGReadDoubleExpression(prop_root, n->getChild(0));
condition->setPrecisionDExpression(exp);
} else {
throw sg_exception("Unknown condition comparison precision child:" + name );
}
}
return condition;
}
static SGCondition *
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, SGComparisonCondition::LESS_THAN,
false);
else if (name == "less-than-equals")
return readComparison(prop_root, node, SGComparisonCondition::GREATER_THAN,
true);
else if (name == "greater-than")
return readComparison(prop_root, node, SGComparisonCondition::GREATER_THAN,
false);
else if (name == "greater-than-equals")
return readComparison(prop_root, node, SGComparisonCondition::LESS_THAN,
true);
else if (name == "equals")
return readComparison(prop_root, node, SGComparisonCondition::EQUALS,
false);
else if (name == "not-equals")
return readComparison(prop_root, node, SGComparisonCondition::EQUALS, true);
else if (name == "false")
return new SGConstantCondition(false);
else if (name == "true")
return new SGConstantCondition(true);
else
return 0;
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGConditional.
////////////////////////////////////////////////////////////////////////
SGConditional::SGConditional ()
: _condition (0)
{
}
SGConditional::~SGConditional ()
{
}
void
SGConditional::setCondition (SGCondition * condition)
{
_condition = condition;
}
bool
SGConditional::test () const
{
return ((_condition == 0) || _condition->test());
}
// The top-level is always an implicit 'and' group
SGCondition *
sgReadCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
return readAndConditions(prop_root, node);
}
// end of condition.cxx