You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdeio/tdeio/ktraderparsetree.cpp

715 lines
17 KiB

/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "ktraderparsetree.h"
namespace TDEIO {
bool ParseTreeOR::eval( ParseContext *_context ) const
{
ParseContext c1( _context );
ParseContext c2( _context );
// don't evaluate both expressions but return immediately
// if the first one of them succeeds. Otherwise queries like
// ((not exist Blah) or (Blah == 'Foo')) do not work, because
// the evaluation of the second term ends up in a fatal error
// (Simon)
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( c1.type != ParseContext::T_BOOL )
return false;
_context->b = c1.b;
_context->type = ParseContext::T_BOOL;
if ( c1.b )
return true;
if ( !m_pRight->eval( &c2 ) )
return false;
if ( c2.type != ParseContext::T_BOOL )
return false;
_context->b = ( c1.b || c2.b );
_context->type = ParseContext::T_BOOL;
return true;
}
bool ParseTreeAND::eval( ParseContext *_context ) const
{
_context->type = ParseContext::T_BOOL;
ParseContext c1( _context );
ParseContext c2( _context );
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( c1.type != ParseContext::T_BOOL )
return false;
if ( !c1.b )
{
_context->b = false;
return true;
}
if ( !m_pRight->eval( &c2 ) )
return false;
if ( c2.type != ParseContext::T_BOOL )
return false;
_context->b = ( c1.b && c2.b );
return true;
}
bool ParseTreeCALC::eval( ParseContext *_context ) const
{
ParseContext c1( _context );
ParseContext c2( _context );
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( !m_pRight->eval( &c2 ) )
return false;
// Bool extension
if ( c1.type != ParseContext::T_NUM && c1.type != ParseContext::T_DOUBLE && c1.type != ParseContext::T_BOOL )
return false;
// Bool extension
if ( c2.type != ParseContext::T_NUM && c2.type != ParseContext::T_DOUBLE && c2.type != ParseContext::T_BOOL )
return false;
// Bool extension
if ( c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_BOOL )
return false;
/**
* Make types compatible
*/
if ( c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_DOUBLE )
{
c1.type = ParseContext::T_DOUBLE;
c1.f = (double)c1.i;
}
else if ( c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_NUM )
{
c2.type = ParseContext::T_DOUBLE;
c2.f = (double)c2.i;
}
// Bool extension
else if ( c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_NUM )
{
c1.type = ParseContext::T_NUM;
if ( c1.b )
c1.i = 1;
else
c1.i = -1;
}
// Bool extension
else if ( c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_DOUBLE )
{
c1.type = ParseContext::T_DOUBLE;
if ( c1.b )
c1.f = 1.0;
else
c1.f = -1.0;
}
// Bool extension
else if ( c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_BOOL )
{
c2.type = ParseContext::T_NUM;
if ( c2.b )
c2.i = 1;
else
c2.i = -1;
}
// Bool extension
else if ( c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_BOOL )
{
c2.type = ParseContext::T_DOUBLE;
if ( c2.b )
c2.f = 1.0;
else
c2.f = -1.0;
}
_context->type = c1.type;
/**
* Calculate
*/
switch( m_cmd )
{
case 1: /* Add */
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->f = ( c1.f + c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->i = ( c1.i + c2.i );
return true;
}
break;
case 2: /* Sub */
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->f = ( c1.f - c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->i = ( c1.i - c2.i );
return true;
}
break;
case 3: /* Mul */
if ( c1.type == ParseContext::T_DOUBLE )
{
//cout << "Double Mult" << endl;
_context->f = ( c1.f * c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->i = ( c1.i * c2.i );
return true;
}
break;
case 4: /* Div */
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->f = ( c1.f / c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->i = ( c1.i / c2.i );
return true;
}
break;
}
return false;
}
bool ParseTreeCMP::eval( ParseContext *_context ) const
{
//cout << "CMP 1 cmd=" << m_cmd << endl;
ParseContext c1( _context );
ParseContext c2( _context );
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( !m_pRight->eval( &c2 ) )
return false;
/**
* Make types compatible
*/
if ( c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_DOUBLE )
{
c1.type = ParseContext::T_DOUBLE;
c1.f = (double)c1.i;
}
else if ( c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_NUM )
{
c2.type = ParseContext::T_DOUBLE;
c2.f = (double)c2.i;
}
/**
* Compare
*/
_context->type = ParseContext::T_BOOL;
switch( m_cmd )
{
case 1: /* EQ */
if ( c1.type != c2.type )
{
_context->b = false;
return true;
}
if ( c1.type == ParseContext::T_STRING )
{
_context->b = ( c1.str == c2.str );
return true;
}
if ( c1.type == ParseContext::T_BOOL )
{
_context->b = ( c1.b == c2.b );
return true;
}
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->b = ( c1.f == c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->b = ( c1.i == c2.i );
return true;
}
break;
case 2: /* NEQ */
if ( c1.type != c2.type )
{
_context->b = true;
return true;
}
if ( c1.type == ParseContext::T_STRING )
{
_context->b = ( c1.str != c2.str );
return true;
}
if ( c1.type == ParseContext::T_BOOL )
{
_context->b = ( c1.b != c2.b );
return true;
}
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->b = ( c1.f != c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->b = ( c1.i != c2.i );
return true;
}
break;
case 3: /* GEQ */
if ( c1.type != c2.type )
{
_context->b = false;
return true;
}
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->b = ( c1.f >= c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->b = ( c1.i >= c2.i );
return true;
}
_context->b = false;
return true;
case 4: /* LEQ */
if ( c1.type != c2.type )
{
_context->b = false;
return true;
}
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->b = ( c1.f <= c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->b = ( c1.i <= c2.i );
return true;
}
_context->b = false;
return true;
case 5: /* < */
if ( c1.type != c2.type )
{
_context->b = false;
return true;
}
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->b = ( c1.f < c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->b = ( c1.i < c2.i );
return true;
}
_context->b = false;
return true;
case 6: /* > */
if ( c1.type != c2.type )
{
_context->b = false;
return true;
}
if ( c1.type == ParseContext::T_DOUBLE )
{
_context->b = ( c1.f > c2.f );
return true;
}
if ( c1.type == ParseContext::T_NUM )
{
_context->b = ( c1.i > c2.i );
return true;
}
_context->b = false;
return true;
}
return false;
}
bool ParseTreeNOT::eval( ParseContext *_context ) const
{
ParseContext c1( _context );
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( c1.type != ParseContext::T_BOOL )
return false;
_context->b = !c1.b;
_context->type = ParseContext::T_BOOL;
return true;
}
bool ParseTreeEXIST::eval( ParseContext *_context ) const
{
_context->type = ParseContext::T_BOOL;
TQVariant prop = _context->service->property( m_id );
_context->b = prop.isValid();
return true;
}
bool ParseTreeMATCH::eval( ParseContext *_context ) const
{
_context->type = ParseContext::T_BOOL;
ParseContext c1( _context );
ParseContext c2( _context );
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( !m_pRight->eval( &c2 ) )
return false;
if ( c1.type != ParseContext::T_STRING || c2.type != ParseContext::T_STRING )
return false;
_context->b = ( c2.str.find( c1.str ) != -1 );
return true;
}
bool ParseTreeIN::eval( ParseContext *_context ) const
{
_context->type = ParseContext::T_BOOL;
ParseContext c1( _context );
ParseContext c2( _context );
if ( !m_pLeft->eval( &c1 ) )
return false;
if ( !m_pRight->eval( &c2 ) )
return false;
if ( (c1.type == ParseContext::T_NUM) &&
(c2.type == ParseContext::T_SEQ) &&
((*(c2.seq.begin())).type() == TQVariant::Int)) {
TQValueList<TQVariant>::ConstIterator it = c2.seq.begin();
TQValueList<TQVariant>::ConstIterator end = c2.seq.end();
_context->b = false;
for (; it != end; it++)
if ((*it).type() == TQVariant::Int &&
(*it).toInt() == c1.i) {
_context->b = true;
break;
}
return true;
}
if ( c1.type == ParseContext::T_DOUBLE &&
c2.type == ParseContext::T_SEQ &&
(*(c2.seq.begin())).type() == TQVariant::Double) {
TQValueList<TQVariant>::ConstIterator it = c2.seq.begin();
TQValueList<TQVariant>::ConstIterator end = c2.seq.end();
_context->b = false;
for (; it != end; it++)
if ((*it).type() == TQVariant::Double &&
(*it).toDouble() == c1.i) {
_context->b = true;
break;
}
return true;
}
if ( c1.type == ParseContext::T_STRING && c2.type == ParseContext::T_STR_SEQ )
{
_context->b = ( c2.strSeq.find( c1.str ) != c2.strSeq.end() );
return true;
}
return false;
}
bool ParseTreeID::eval( ParseContext *_context ) const
{
TQVariant prop = _context->service->property( m_str );
if ( !prop.isValid() )
return false;
if ( prop.type() == TQVariant::String )
{
_context->str = prop.toString();
_context->type = ParseContext::T_STRING;
return true;
}
if ( prop.type() == TQVariant::Int )
{
_context->i = prop.toInt();
_context->type = ParseContext::T_NUM;
return true;
}
if ( prop.type() == TQVariant::Bool )
{
_context->b = prop.toBool();
_context->type = ParseContext::T_BOOL;
return true;
}
if ( prop.type() == TQVariant::Double )
{
_context->f = prop.toDouble();
_context->type = ParseContext::T_DOUBLE;
return true;
}
if ( prop.type() == TQVariant::List )
{
_context->seq = prop.toList();
_context->type = ParseContext::T_SEQ;
return true;
}
if ( prop.type() == TQVariant::StringList )
{
_context->strSeq = prop.toStringList();
_context->type = ParseContext::T_STR_SEQ;
return true;
}
// Value has unknown type
return false;
}
bool ParseTreeMIN2::eval( ParseContext *_context ) const
{
_context->type = ParseContext::T_DOUBLE;
TQVariant prop = _context->service->property( m_strId );
if ( !prop.isValid() )
return false;
if ( !_context->initMaxima( m_strId ) )
return false;
TQMap<TQString,PreferencesMaxima>::Iterator it = _context->maxima.find( m_strId );
if ( it == _context->maxima.end() )
return false;
if ( prop.type() == TQVariant::Int && it.data().type == PreferencesMaxima::PM_INT )
{
_context->f = (double)( prop.toInt() - it.data().iMin ) /
(double)(it.data().iMax - it.data().iMin ) * (-2.0) + 1.0;
return true;
}
else if ( prop.type() == TQVariant::Double && it.data().type == PreferencesMaxima::PM_DOUBLE )
{
_context->f = ( prop.toDouble() - it.data().fMin ) / (it.data().fMax - it.data().fMin )
* (-2.0) + 1.0;
return true;
}
return false;
}
bool ParseTreeMAX2::eval( ParseContext *_context ) const
{
_context->type = ParseContext::T_DOUBLE;
TQVariant prop = _context->service->property( m_strId );
if ( !prop.isValid() )
return false;
// Create extrema
if ( !_context->initMaxima( m_strId ) )
return false;
// Find extrema
TQMap<TQString,PreferencesMaxima>::Iterator it = _context->maxima.find( m_strId );
if ( it == _context->maxima.end() )
return false;
if ( prop.type() == TQVariant::Int && it.data().type == PreferencesMaxima::PM_INT )
{
_context->f = (double)( prop.toInt() - it.data().iMin ) /
(double)(it.data().iMax - it.data().iMin ) * 2.0 - 1.0;
return true;
}
else if ( prop.type() == TQVariant::Double && it.data().type == PreferencesMaxima::PM_DOUBLE )
{
_context->f = ( prop.toDouble() - it.data().fMin ) /
(it.data().fMax - it.data().fMin ) * 2.0 - 1.0;
return true;
}
return false;
}
int matchConstraint( const ParseTreeBase *_tree, const KService::Ptr &_service,
const KServiceTypeProfile::OfferList& _list )
{
// Empty tree matches always
if ( !_tree )
return 1;
TQMap<TQString,PreferencesMaxima> maxima;
ParseContext c( _service, _list, maxima );
// Error during evaluation ?
if ( !_tree->eval( &c ) )
return -1;
// Did we get a bool ?
if ( c.type != ParseContext::T_BOOL )
return -1;
return ( c.b ? 1 : 0 );
}
PreferencesReturn matchPreferences( const ParseTreeBase *_tree, const KService::Ptr &_service,
const KServiceTypeProfile::OfferList& _list )
{
// By default: error
PreferencesReturn ret;
if ( !_tree )
return ret;
TQMap<TQString,PreferencesMaxima> maxima;
ParseContext c( _service, _list, maxima );
if ( !_tree->eval( &c ) )
return ret;
// Did we get a numeric return value ?
if ( c.type == ParseContext::T_NUM )
{
ret.type = PreferencesReturn::PRT_DOUBLE;
ret.f = (double)c.i;
}
else if ( c.type == ParseContext::T_DOUBLE )
{
ret.type = PreferencesReturn::PRT_DOUBLE;
ret.f = c.f;
}
return ret;
}
bool ParseContext::initMaxima( const TQString& _prop )
{
// Is the property known ?
TQVariant prop = service->property( _prop );
if ( !prop.isValid() )
return false;
// Numeric ?
if ( prop.type() != TQVariant::Int && prop.type() != TQVariant::Double )
return false;
// Did we cache the result ?
TQMap<TQString,PreferencesMaxima>::Iterator it = maxima.find( _prop );
if ( it != maxima.end() )
return ( it.data().type == PreferencesMaxima::PM_DOUBLE ||
it.data().type == PreferencesMaxima::PM_INT );
// Double or Int ?
PreferencesMaxima extrema;
if ( prop.type() == TQVariant::Int )
extrema.type = PreferencesMaxima::PM_INVALID_INT;
else
extrema.type = PreferencesMaxima::PM_INVALID_DOUBLE;
// Iterate over all offers
KServiceTypeProfile::OfferList::ConstIterator oit = offers.begin();
for( ; oit != offers.end(); ++oit )
{
TQVariant p = (*oit).service()->property( _prop );
if ( p.isValid() )
{
// Determine new maximum/minimum
if ( extrema.type == PreferencesMaxima::PM_INVALID_INT )
{
extrema.type = PreferencesMaxima::PM_INT;
extrema.iMin = p.toInt();
extrema.iMax = p.toInt();
}
// Correct existing extrema
else if ( extrema.type == PreferencesMaxima::PM_INT )
{
if ( p.toInt() < extrema.iMin )
extrema.iMin = p.toInt();
if ( p.toInt() > extrema.iMax )
extrema.iMax = p.toInt();
}
// Determine new maximum/minimum
else if ( extrema.type == PreferencesMaxima::PM_INVALID_DOUBLE )
{
extrema.type = PreferencesMaxima::PM_DOUBLE;
extrema.fMin = p.toDouble();
extrema.fMax = p.toDouble();
}
// Correct existing extrema
else if ( extrema.type == PreferencesMaxima::PM_DOUBLE )
{
if ( p.toDouble() < it.data().fMin )
extrema.fMin = p.toDouble();
if ( p.toDouble() > it.data().fMax )
extrema.fMax = p.toDouble();
}
}
}
// Cache the result
maxima.insert( _prop, extrema );
// Did we succeed ?
return ( extrema.type == PreferencesMaxima::PM_DOUBLE ||
extrema.type == PreferencesMaxima::PM_INT );
}
}