Files
simgear/simgear/props/condition.cxx
2014-06-22 15:35:31 +02:00

663 lines
17 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/structure/SGExpression.hxx>
using std::istream;
using std::ostream;
using std::string;
/**
* 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(); }
virtual void collectDependentProperties(std::set<const SGPropertyNode*>& props) const
{ props.insert(_node.get()); }
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;
virtual void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
private:
SGConditionRef _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);
virtual void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
private:
std::vector<SGConditionRef> _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);
virtual void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
private:
std::vector<SGConditionRef> _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);
virtual void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
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());
}
void
SGNotCondition::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
{
_condition->collectDependentProperties(props);
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGAndCondition.
////////////////////////////////////////////////////////////////////////
SGAndCondition::SGAndCondition ()
{
}
SGAndCondition::~SGAndCondition ()
{
}
bool
SGAndCondition::test () const
{
for( size_t i = 0; i < _conditions.size(); i++ )
{
if (!_conditions[i]->test())
return false;
}
return true;
}
void
SGAndCondition::addCondition (SGCondition * condition)
{
_conditions.push_back(condition);
}
void
SGAndCondition::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
{
for( size_t i = 0; i < _conditions.size(); i++ )
_conditions[i]->collectDependentProperties(props);
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGOrCondition.
////////////////////////////////////////////////////////////////////////
SGOrCondition::SGOrCondition ()
{
}
SGOrCondition::~SGOrCondition ()
{
}
bool
SGOrCondition::test () const
{
for( size_t i = 0; i < _conditions.size(); i++ )
{
if (_conditions[i]->test())
return true;
}
return false;
}
void
SGOrCondition::addCondition (SGCondition * condition)
{
_conditions.push_back(condition);
}
void
SGOrCondition::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
{
for( size_t i = 0; i < _conditions.size(); i++ )
_conditions[i]->collectDependentProperties(props);
}
////////////////////////////////////////////////////////////////////////
// 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 ? std::abs(precision->getIntValue()/2) : 0 );
case props::LONG:
return doComp<long>(left->getLongValue(), right->getLongValue(),
precision ? std::abs(precision->getLongValue()/2L) : 0L );
case props::FLOAT:
return doComp<float>(left->getFloatValue(), right->getFloatValue(),
precision ? std::fabs(precision->getFloatValue()/2.0f) : 0.0f );
case props::DOUBLE:
return doComp<double>(left->getDoubleValue(), right->getDoubleValue(),
precision ? std::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;
}
void
SGComparisonCondition::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
{
if (_left_dexp)
_left_dexp->collectDependentProperties(props);
else
props.insert(_left_property);
if (_right_dexp)
_right_dexp->collectDependentProperties(props);
else
props.insert(_right_property);
if (_precision_dexp)
_precision_dexp->collectDependentProperties(props);
else if (_precision_property)
props.insert(_precision_property);
}
////////////////////////////////////////////////////////////////////////
// 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