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.
tdepim/akregator/src/articlefilter.cpp

716 lines
18 KiB

/*
* articlefilter.cpp
*
* Copyright (c) 2004, 2005 Frerich Raabe <raabe@kde.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSETQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "articlefilter.h"
#include "article.h"
#include "shared.h"
#include <kapplication.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kurl.h>
#include <tqregexp.h>
namespace Akregator {
namespace Filters {
TQString Criterion::subjectToString(Subject subj)
{
switch (subj)
{
case Title:
return TQString::tqfromLatin1("Title");
case Link:
return TQString::tqfromLatin1("Link");
case Author:
return TQString::tqfromLatin1("Author");
case Description:
return TQString::tqfromLatin1("Description");
case tqStatus:
return TQString::tqfromLatin1("tqStatus");
case KeepFlag:
return TQString::tqfromLatin1("KeepFlag");
default: // should never happen (TM)
return TQString::tqfromLatin1("Description");
}
}
Criterion::Subject Criterion::stringToSubject(const TQString& subjStr)
{
if (subjStr == TQString::tqfromLatin1("Title"))
return Title;
else if (subjStr == TQString::tqfromLatin1("Link"))
return Link;
else if (subjStr == TQString::tqfromLatin1("Description"))
return Description;
else if (subjStr == TQString::tqfromLatin1("Author"))
return Author;
else if (subjStr == TQString::tqfromLatin1("tqStatus"))
return tqStatus;
else if (subjStr == TQString::tqfromLatin1("KeepFlag"))
return KeepFlag;
// hopefully never reached
return Description;
}
TQString Criterion::predicateToString(Predicate pred)
{
switch (pred)
{
case Contains:
return TQString::tqfromLatin1("Contains");
case Equals:
return TQString::tqfromLatin1("Equals");
case Matches:
return TQString::tqfromLatin1("Matches");
case Negation:
return TQString::tqfromLatin1("Negation");
default:// hopefully never reached
return TQString::tqfromLatin1("Contains");
}
}
Criterion::Predicate Criterion::stringToPredicate(const TQString& predStr)
{
if (predStr == TQString::tqfromLatin1("Contains"))
return Contains;
else if (predStr == TQString::tqfromLatin1("Equals"))
return Equals;
else if (predStr == TQString::tqfromLatin1("Matches"))
return Matches;
else if (predStr == TQString::tqfromLatin1("Negation"))
return Negation;
// hopefully never reached
return Contains;
}
Criterion::Criterion()
{
}
Criterion::Criterion( Subject subject, Predicate predicate, const TQVariant &object )
: m_subject( subject )
, m_predicate( predicate )
, m_object( object )
{
}
void Criterion::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("subject"), subjectToString(m_subject));
config->writeEntry(TQString::tqfromLatin1("predicate"), predicateToString(m_predicate));
config->writeEntry(TQString::tqfromLatin1("objectType"), TQString(m_object.typeName()));
config->writeEntry(TQString::tqfromLatin1("objectValue"), m_object);
}
void Criterion::readConfig(KConfig* config)
{
m_subject = stringToSubject(config->readEntry(TQString::tqfromLatin1("subject")));
m_predicate = stringToPredicate(config->readEntry(TQString::tqfromLatin1("predicate")));
TQVariant::Type type = TQVariant::nameToType(config->readEntry(TQString::tqfromLatin1("objType")).ascii());
if (type != TQVariant::Invalid)
{
m_object = config->readPropertyEntry(TQString::tqfromLatin1("objectValue"), type);
}
}
bool Criterion::satisfiedBy( const Article &article ) const
{
TQVariant concreteSubject;
switch ( m_subject ) {
case Title:
concreteSubject = TQVariant(article.title());
break;
case Description:
concreteSubject = TQVariant(article.description());
break;
case Author:
concreteSubject = TQVariant(article.author());
break;
case Link:
// ### Maybe use prettyURL here?
concreteSubject = TQVariant(article.link().url());
break;
case tqStatus:
concreteSubject = TQVariant(article.status());
break;
case KeepFlag:
concreteSubject = TQVariant(article.keep());
default:
break;
}
bool satisfied = false;
const Predicate predicateType = static_cast<Predicate>( m_predicate & ~Negation );
TQString subjectType=concreteSubject.typeName();
switch ( predicateType ) {
case Contains:
satisfied = concreteSubject.toString().find( m_object.toString(), 0, false ) != -1;
break;
case Equals:
if (subjectType=="int")
satisfied = concreteSubject.toInt() == m_object.toInt();
else
satisfied = concreteSubject.toString() == m_object.toString();
break;
case Matches:
satisfied = TQRegExp( m_object.toString() ).search( concreteSubject.toString() ) != -1;
break;
default:
kdDebug() << "Internal inconsistency; predicateType should never be Negation" << endl;
break;
}
if ( m_predicate & Negation ) {
satisfied = !satisfied;
}
return satisfied;
}
Criterion::Subject Criterion::subject() const
{
return m_subject;
}
Criterion::Predicate Criterion::predicate() const
{
return m_predicate;
}
TQVariant Criterion::object() const
{
return m_object;
}
ArticleMatcher::ArticleMatcher()
: m_association( None )
{
}
ArticleMatcher::~ArticleMatcher()
{
}
bool ArticleMatcher::matchesAll() const
{
return m_criteria.isEmpty();
}
ArticleMatcher* ArticleMatcher::clone() const
{
return new ArticleMatcher(*this);
}
ArticleMatcher::ArticleMatcher( const TQValueList<Criterion> &criteria, Association assoc)
: m_criteria( criteria )
, m_association( assoc )
{
}
ArticleMatcher& ArticleMatcher::operator=(const ArticleMatcher& other)
{
m_association = other.m_association;
m_criteria = other.m_criteria;
return *this;
}
ArticleMatcher::ArticleMatcher(const ArticleMatcher& other) : AbstractMatcher(other)
{
*this = other;
}
bool ArticleMatcher::matches( const Article &a ) const
{
switch ( m_association ) {
case LogicalOr:
return anyCriterionMatches( a );
case LogicalAnd:
return allCriteriaMatch( a );
default:
break;
}
return true;
}
void ArticleMatcher::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("matcherAssociation"), associationToString(m_association));
config->writeEntry(TQString::tqfromLatin1("matcherCriteriaCount"), m_criteria.count());
int index = 0;
for (TQValueList<Criterion>::ConstIterator it = m_criteria.begin(); it != m_criteria.end(); ++it)
{
config->setGroup(config->group()+TQString::tqfromLatin1("_Criterion")+TQString::number(index));
(*it).writeConfig(config);
++index;
}
}
void ArticleMatcher::readConfig(KConfig* config)
{
m_criteria.clear();
m_association = stringToAssociation(config->readEntry(TQString::tqfromLatin1("matcherAssociation")));
int count = config->readNumEntry(TQString::tqfromLatin1("matcherCriteriaCount"), 0);
for (int i = 0; i < count; ++i)
{
Criterion c;
config->setGroup(config->group()+TQString::tqfromLatin1("_Criterion")+TQString::number(i));
c.readConfig(config);
m_criteria.append(c);
}
}
bool ArticleMatcher::operator==(const AbstractMatcher& other) const
{
AbstractMatcher* ptr = const_cast<AbstractMatcher*>(&other);
ArticleMatcher* o = dynamic_cast<ArticleMatcher*>(ptr);
if (!o)
return false;
else
return m_association == o->m_association && m_criteria == o->m_criteria;
}
bool ArticleMatcher::operator!=(const AbstractMatcher& other) const
{
return !(*this == other);
}
bool ArticleMatcher::anyCriterionMatches( const Article &a ) const
{
if (m_criteria.count()==0)
return true;
TQValueList<Criterion>::ConstIterator it = m_criteria.begin();
TQValueList<Criterion>::ConstIterator end = m_criteria.end();
for ( ; it != end; ++it ) {
if ( ( *it ).satisfiedBy( a ) ) {
return true;
}
}
return false;
}
bool ArticleMatcher::allCriteriaMatch( const Article &a ) const
{
if (m_criteria.count()==0)
return true;
TQValueList<Criterion>::ConstIterator it = m_criteria.begin();
TQValueList<Criterion>::ConstIterator end = m_criteria.end();
for ( ; it != end; ++it ) {
if ( !( *it ).satisfiedBy( a ) ) {
return false;
}
}
return true;
}
ArticleMatcher::Association ArticleMatcher::stringToAssociation(const TQString& assocStr)
{
if (assocStr == TQString::tqfromLatin1("LogicalAnd"))
return LogicalAnd;
else if (assocStr == TQString::tqfromLatin1("LogicalOr"))
return LogicalOr;
else
return None;
}
TQString ArticleMatcher::associationToString(Association association)
{
switch (association)
{
case LogicalAnd:
return TQString::tqfromLatin1("LogicalAnd");
case LogicalOr:
return TQString::tqfromLatin1("LogicalOr");
default:
return TQString::tqfromLatin1("None");
}
}
class TagMatcher::TagMatcherPrivate
{
public:
TQString tagID;
bool operator==(const TagMatcherPrivate& other) const
{
return tagID == other.tagID;
}
};
TagMatcher::TagMatcher(const TQString& tagID) : d(new TagMatcherPrivate)
{
d->tagID = tagID;
}
TagMatcher::TagMatcher() : d(new TagMatcherPrivate)
{
}
TagMatcher::~TagMatcher()
{
delete d;
d = 0;
}
bool TagMatcher::matches(const Article& article) const
{
return article.hasTag(d->tagID);
}
TagMatcher* TagMatcher::clone() const
{
return new TagMatcher(*this);
}
TagMatcher::TagMatcher(const TagMatcher& other) : AbstractMatcher(other), d(0)
{
*this = other;
}
void TagMatcher::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("matcherType"), TQString::tqfromLatin1("TagMatcher"));
config->writeEntry(TQString::tqfromLatin1("matcherParams"), d->tagID);
}
void TagMatcher::readConfig(KConfig* config)
{
d->tagID = config->readEntry(TQString::tqfromLatin1("matcherParams"));
}
bool TagMatcher::operator==(const AbstractMatcher& other) const
{
AbstractMatcher* ptr = const_cast<AbstractMatcher*>(&other);
TagMatcher* tagFilter = dynamic_cast<TagMatcher*>(ptr);
return tagFilter ? *d == *(tagFilter->d) : false;
}
bool TagMatcher::operator!=(const AbstractMatcher &other) const
{
return !(*this == other);
}
TagMatcher& TagMatcher::operator=(const TagMatcher& other)
{
d = new TagMatcherPrivate;
*d = *(other.d);
return *this;
}
void DeleteAction::exec(Article& article)
{
if (!article.isNull())
article.setDeleted();
}
SeStatusAction::SeStatusAction(int status) : m_status(status)
{
}
void SeStatusAction::exec(Article& article)
{
if (!article.isNull())
article.setqStatus(m_status);
}
int SeStatusAction::status() const
{
return m_status;
}
void SeStatusAction::setqStatus(int status)
{
m_status = status;
}
void SeStatusAction::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("actionType"), TQString::tqfromLatin1("SeStatusAction"));
config->writeEntry(TQString::tqfromLatin1("actionParams"), m_status);
}
void SeStatusAction::readConfig(KConfig* config)
{
m_status = config->readNumEntry(TQString::tqfromLatin1("actionParams"), Article::Read);
}
bool SeStatusAction::operator==(const AbstractAction& other)
{
AbstractAction* ptr = const_cast<AbstractAction*>(&other);
SeStatusAction* o = dynamic_cast<SeStatusAction*>(ptr);
if (!o)
return false;
else
return m_status == o->m_status;
}
AssignTagAction::AssignTagAction(const TQString& tagID) : m_tagID(tagID)
{
}
void AssignTagAction::exec(Article& article)
{
if (!article.isNull())
article.addTag(m_tagID);
}
class ArticleFilter::ArticleFilterPrivate : public Shared
{
public:
AbstractAction* action;
AbstractMatcher* matcher;
TQString name;
int id;
};
ArticleFilter::ArticleFilter() : d(new ArticleFilterPrivate)
{
d->id = KApplication::random();
d->action = 0;
d->matcher = 0;
}
ArticleFilter::ArticleFilter(const AbstractMatcher& matcher, const AbstractAction& action) : d(new ArticleFilterPrivate)
{
d->id = KApplication::random();
d->matcher = matcher.clone();
d->action = action.clone();
}
ArticleFilter::ArticleFilter(const ArticleFilter& other)
{
*this = other;
}
ArticleFilter::~ArticleFilter()
{
if (d->deref())
{
delete d->action;
delete d->matcher;
delete d;
d = 0;
}
}
AbstractMatcher* ArticleFilter::matcher() const
{
return d->matcher;
}
AbstractAction* ArticleFilter::action() const
{
return d->action;
}
void ArticleFilter::setMatcher(const AbstractMatcher& matcher)
{
delete d->matcher;
d->matcher = matcher.clone();
}
void ArticleFilter::setAction(const AbstractAction& action)
{
delete d->action;
d->action = action.clone();
}
ArticleFilter& ArticleFilter::operator=(const ArticleFilter& other)
{
if (this != &other)
{
other.d->ref();
if (d && d->deref())
delete d;
d = other.d;
}
return *this;
}
int ArticleFilter::id() const
{
return d->id;
}
bool ArticleFilter::operator==(const ArticleFilter& other) const
{
return *(d->matcher) == *(other.d->matcher) && *(d->action) == *(other.d->action) && d->name == other.d->name;
}
void ArticleFilterList::writeConfig(KConfig* config) const
{
config->setGroup(TQString::tqfromLatin1("Filters"));
config->writeEntry(TQString::tqfromLatin1("count"), count());
int index = 0;
for (ArticleFilterList::ConstIterator it = begin(); it != end(); ++it)
{
config->setGroup(TQString::tqfromLatin1("Filters_")+TQString::number(index));
(*it).writeConfig(config);
++index;
}
}
void ArticleFilterList::readConfig(KConfig* config)
{
clear();
config->setGroup(TQString::tqfromLatin1("Filters"));
int count = config->readNumEntry(TQString::tqfromLatin1("count"), 0);
for (int i = 0; i < count; ++i)
{
config->setGroup(TQString::tqfromLatin1("Filters_")+TQString::number(i));
ArticleFilter filter;
filter.readConfig(config);
append(filter);
}
}
void AssignTagAction::readConfig(KConfig* config)
{
m_tagID = config->readEntry(TQString::tqfromLatin1("actionParams"));
}
void AssignTagAction::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("actionType"), TQString::tqfromLatin1("AssignTagAction"));
config->writeEntry(TQString::tqfromLatin1("actionParams"), m_tagID);
}
bool AssignTagAction::operator==(const AbstractAction& other)
{
AbstractAction* ptr = const_cast<AbstractAction*>(&other);
AssignTagAction* o = dynamic_cast<AssignTagAction*>(ptr);
if (!o)
return false;
else
return m_tagID == o->m_tagID;
}
const TQString& AssignTagAction::tagID() const
{
return m_tagID;
}
void AssignTagAction::setTagID(const TQString& tagID)
{
m_tagID = tagID;
}
void DeleteAction::readConfig(KConfig* /*config*/)
{
}
void DeleteAction::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("actionType"), TQString::tqfromLatin1("DeleteAction"));
}
bool DeleteAction::operator==(const AbstractAction& other)
{
AbstractAction* ptr = const_cast<AbstractAction*>(&other);
DeleteAction* o = dynamic_cast<DeleteAction*>(ptr);
return o != 0;
}
void ArticleFilter::readConfig(KConfig* config)
{
delete d->matcher;
d->matcher = 0;
delete d->action;
d->action = 0;
d->name = config->readEntry(TQString::tqfromLatin1("name"));
d->id = config->readNumEntry(TQString::tqfromLatin1("id"), 0);
TQString matcherType = config->readEntry(TQString::tqfromLatin1("matcherType"));
if (matcherType == TQString::tqfromLatin1("TagMatcher"))
d->matcher = new TagMatcher();
else if (matcherType == TQString::tqfromLatin1("ArticleMatcher"))
d->matcher = new ArticleMatcher();
if (d->matcher)
d->matcher->readConfig(config);
TQString actionType = config->readEntry(TQString::tqfromLatin1("actionType"));
if (actionType == TQString::tqfromLatin1("AssignTagAction"))
d->action = new AssignTagAction();
else if (actionType == TQString::tqfromLatin1("DeleteAction"))
d->action = new DeleteAction();
else if (actionType == TQString::tqfromLatin1("SeStatusAction"))
d->action = new SeStatusAction();
if (d->action)
d->action->readConfig(config);
}
void ArticleFilter::writeConfig(KConfig* config) const
{
config->writeEntry(TQString::tqfromLatin1("name"), d->name);
config->writeEntry(TQString::tqfromLatin1("id"), d->id);
d->matcher->writeConfig(config);
d->action->writeConfig(config);
}
void ArticleFilter::setName(const TQString& name)
{
d->name = name;
}
const TQString& ArticleFilter::name() const
{
return d->name;
}
void ArticleFilter::applyTo(Article& article) const
{
if (d->matcher && d->action && d->matcher->matches(article))
d->action->exec(article);
}
} //namespace Filters
} //namespace Akregator