akregator/src
articlefilter.cpp00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "articlefilter.h"
00028 #include "article.h"
00029 #include "shared.h"
00030
00031 #include <kapplication.h>
00032 #include <kconfig.h>
00033 #include <kdebug.h>
00034 #include <kurl.h>
00035
00036 #include <qregexp.h>
00037
00038 namespace Akregator {
00039 namespace Filters {
00040
00041 QString Criterion::subjectToString(Subject subj)
00042 {
00043 switch (subj)
00044 {
00045 case Title:
00046 return QString::fromLatin1("Title");
00047 case Link:
00048 return QString::fromLatin1("Link");
00049 case Author:
00050 return QString::fromLatin1("Author");
00051 case Description:
00052 return QString::fromLatin1("Description");
00053 case Status:
00054 return QString::fromLatin1("Status");
00055 case KeepFlag:
00056 return QString::fromLatin1("KeepFlag");
00057 default:
00058 return QString::fromLatin1("Description");
00059 }
00060 }
00061
00062 Criterion::Subject Criterion::stringToSubject(const QString& subjStr)
00063 {
00064 if (subjStr == QString::fromLatin1("Title"))
00065 return Title;
00066 else if (subjStr == QString::fromLatin1("Link"))
00067 return Link;
00068 else if (subjStr == QString::fromLatin1("Description"))
00069 return Description;
00070 else if (subjStr == QString::fromLatin1("Author"))
00071 return Author;
00072 else if (subjStr == QString::fromLatin1("Status"))
00073 return Status;
00074 else if (subjStr == QString::fromLatin1("KeepFlag"))
00075 return KeepFlag;
00076
00077
00078 return Description;
00079 }
00080
00081 QString Criterion::predicateToString(Predicate pred)
00082 {
00083 switch (pred)
00084 {
00085 case Contains:
00086 return QString::fromLatin1("Contains");
00087 case Equals:
00088 return QString::fromLatin1("Equals");
00089 case Matches:
00090 return QString::fromLatin1("Matches");
00091 case Negation:
00092 return QString::fromLatin1("Negation");
00093 default:
00094 return QString::fromLatin1("Contains");
00095 }
00096 }
00097
00098 Criterion::Predicate Criterion::stringToPredicate(const QString& predStr)
00099 {
00100 if (predStr == QString::fromLatin1("Contains"))
00101 return Contains;
00102 else if (predStr == QString::fromLatin1("Equals"))
00103 return Equals;
00104 else if (predStr == QString::fromLatin1("Matches"))
00105 return Matches;
00106 else if (predStr == QString::fromLatin1("Negation"))
00107 return Negation;
00108
00109
00110 return Contains;
00111 }
00112
00113 Criterion::Criterion()
00114 {
00115 }
00116
00117 Criterion::Criterion( Subject subject, Predicate predicate, const QVariant &object )
00118 : m_subject( subject )
00119 , m_predicate( predicate )
00120 , m_object( object )
00121 {
00122
00123 }
00124
00125 void Criterion::writeConfig(KConfig* config) const
00126 {
00127 config->writeEntry(QString::fromLatin1("subject"), subjectToString(m_subject));
00128
00129 config->writeEntry(QString::fromLatin1("predicate"), predicateToString(m_predicate));
00130
00131 config->writeEntry(QString::fromLatin1("objectType"), QString(m_object.typeName()));
00132
00133 config->writeEntry(QString::fromLatin1("objectValue"), m_object);
00134 }
00135
00136 void Criterion::readConfig(KConfig* config)
00137 {
00138 m_subject = stringToSubject(config->readEntry(QString::fromLatin1("subject")));
00139 m_predicate = stringToPredicate(config->readEntry(QString::fromLatin1("predicate")));
00140 QVariant::Type type = QVariant::nameToType(config->readEntry(QString::fromLatin1("objType")).ascii());
00141
00142 if (type != QVariant::Invalid)
00143 {
00144 m_object = config->readPropertyEntry(QString::fromLatin1("objectValue"), type);
00145 }
00146 }
00147
00148 bool Criterion::satisfiedBy( const Article &article ) const
00149 {
00150 QVariant concreteSubject;
00151
00152 switch ( m_subject ) {
00153 case Title:
00154 concreteSubject = QVariant(article.title());
00155 break;
00156 case Description:
00157 concreteSubject = QVariant(article.description());
00158 break;
00159 case Author:
00160 concreteSubject = QVariant(article.author());
00161 break;
00162 case Link:
00163
00164 concreteSubject = QVariant(article.link().url());
00165 break;
00166 case Status:
00167 concreteSubject = QVariant(article.status());
00168 break;
00169 case KeepFlag:
00170 concreteSubject = QVariant(article.keep());
00171 default:
00172 break;
00173 }
00174
00175 bool satisfied = false;
00176
00177 const Predicate predicateType = static_cast<Predicate>( m_predicate & ~Negation );
00178 QString subjectType=concreteSubject.typeName();
00179
00180 switch ( predicateType ) {
00181 case Contains:
00182 satisfied = concreteSubject.toString().find( m_object.toString(), 0, false ) != -1;
00183 break;
00184 case Equals:
00185 if (subjectType=="int")
00186 satisfied = concreteSubject.toInt() == m_object.toInt();
00187 else
00188 satisfied = concreteSubject.toString() == m_object.toString();
00189 break;
00190 case Matches:
00191 satisfied = QRegExp( m_object.toString() ).search( concreteSubject.toString() ) != -1;
00192 break;
00193 default:
00194 kdDebug() << "Internal inconsistency; predicateType should never be Negation" << endl;
00195 break;
00196 }
00197
00198 if ( m_predicate & Negation ) {
00199 satisfied = !satisfied;
00200 }
00201
00202 return satisfied;
00203 }
00204
00205 Criterion::Subject Criterion::subject() const
00206 {
00207 return m_subject;
00208 }
00209
00210 Criterion::Predicate Criterion::predicate() const
00211 {
00212 return m_predicate;
00213 }
00214
00215 QVariant Criterion::object() const
00216 {
00217 return m_object;
00218 }
00219
00220 ArticleMatcher::ArticleMatcher()
00221 : m_association( None )
00222 {
00223 }
00224
00225 ArticleMatcher::~ArticleMatcher()
00226 {
00227 }
00228
00229 bool ArticleMatcher::matchesAll() const
00230 {
00231 return m_criteria.isEmpty();
00232 }
00233
00234 ArticleMatcher* ArticleMatcher::clone() const
00235 {
00236 return new ArticleMatcher(*this);
00237 }
00238
00239 ArticleMatcher::ArticleMatcher( const QValueList<Criterion> &criteria, Association assoc)
00240 : m_criteria( criteria )
00241 , m_association( assoc )
00242 {
00243 }
00244
00245 ArticleMatcher& ArticleMatcher::operator=(const ArticleMatcher& other)
00246 {
00247 m_association = other.m_association;
00248 m_criteria = other.m_criteria;
00249 return *this;
00250 }
00251
00252 ArticleMatcher::ArticleMatcher(const ArticleMatcher& other) : AbstractMatcher(other)
00253 {
00254 *this = other;
00255 }
00256
00257 bool ArticleMatcher::matches( const Article &a ) const
00258 {
00259 switch ( m_association ) {
00260 case LogicalOr:
00261 return anyCriterionMatches( a );
00262 case LogicalAnd:
00263 return allCriteriaMatch( a );
00264 default:
00265 break;
00266 }
00267 return true;
00268 }
00269
00270 void ArticleMatcher::writeConfig(KConfig* config) const
00271 {
00272 config->writeEntry(QString::fromLatin1("matcherAssociation"), associationToString(m_association));
00273
00274 config->writeEntry(QString::fromLatin1("matcherCriteriaCount"), m_criteria.count());
00275
00276 int index = 0;
00277
00278 for (QValueList<Criterion>::ConstIterator it = m_criteria.begin(); it != m_criteria.end(); ++it)
00279 {
00280 config->setGroup(config->group()+QString::fromLatin1("_Criterion")+QString::number(index));
00281 (*it).writeConfig(config);
00282 ++index;
00283 }
00284 }
00285
00286 void ArticleMatcher::readConfig(KConfig* config)
00287 {
00288 m_criteria.clear();
00289 m_association = stringToAssociation(config->readEntry(QString::fromLatin1("matcherAssociation")));
00290
00291 int count = config->readNumEntry(QString::fromLatin1("matcherCriteriaCount"), 0);
00292
00293 for (int i = 0; i < count; ++i)
00294 {
00295 Criterion c;
00296 config->setGroup(config->group()+QString::fromLatin1("_Criterion")+QString::number(i));
00297 c.readConfig(config);
00298 m_criteria.append(c);
00299 }
00300 }
00301
00302 bool ArticleMatcher::operator==(const AbstractMatcher& other) const
00303 {
00304 AbstractMatcher* ptr = const_cast<AbstractMatcher*>(&other);
00305 ArticleMatcher* o = dynamic_cast<ArticleMatcher*>(ptr);
00306 if (!o)
00307 return false;
00308 else
00309 return m_association == o->m_association && m_criteria == o->m_criteria;
00310 }
00311 bool ArticleMatcher::operator!=(const AbstractMatcher& other) const
00312 {
00313 return !(*this == other);
00314 }
00315
00316 bool ArticleMatcher::anyCriterionMatches( const Article &a ) const
00317 {
00318 if (m_criteria.count()==0)
00319 return true;
00320 QValueList<Criterion>::ConstIterator it = m_criteria.begin();
00321 QValueList<Criterion>::ConstIterator end = m_criteria.end();
00322 for ( ; it != end; ++it ) {
00323 if ( ( *it ).satisfiedBy( a ) ) {
00324 return true;
00325 }
00326 }
00327 return false;
00328 }
00329
00330 bool ArticleMatcher::allCriteriaMatch( const Article &a ) const
00331 {
00332 if (m_criteria.count()==0)
00333 return true;
00334 QValueList<Criterion>::ConstIterator it = m_criteria.begin();
00335 QValueList<Criterion>::ConstIterator end = m_criteria.end();
00336 for ( ; it != end; ++it ) {
00337 if ( !( *it ).satisfiedBy( a ) ) {
00338 return false;
00339 }
00340 }
00341 return true;
00342 }
00343
00344 ArticleMatcher::Association ArticleMatcher::stringToAssociation(const QString& assocStr)
00345 {
00346 if (assocStr == QString::fromLatin1("LogicalAnd"))
00347 return LogicalAnd;
00348 else if (assocStr == QString::fromLatin1("LogicalOr"))
00349 return LogicalOr;
00350 else
00351 return None;
00352 }
00353
00354 QString ArticleMatcher::associationToString(Association association)
00355 {
00356 switch (association)
00357 {
00358 case LogicalAnd:
00359 return QString::fromLatin1("LogicalAnd");
00360 case LogicalOr:
00361 return QString::fromLatin1("LogicalOr");
00362 default:
00363 return QString::fromLatin1("None");
00364 }
00365 }
00366
00367
00368 class TagMatcher::TagMatcherPrivate
00369 {
00370 public:
00371 QString tagID;
00372 bool operator==(const TagMatcherPrivate& other) const
00373 {
00374 return tagID == other.tagID;
00375 }
00376 };
00377
00378 TagMatcher::TagMatcher(const QString& tagID) : d(new TagMatcherPrivate)
00379 {
00380 d->tagID = tagID;
00381 }
00382
00383 TagMatcher::TagMatcher() : d(new TagMatcherPrivate)
00384 {
00385 }
00386
00387 TagMatcher::~TagMatcher()
00388 {
00389 delete d;
00390 d = 0;
00391 }
00392
00393 bool TagMatcher::matches(const Article& article) const
00394 {
00395 return article.hasTag(d->tagID);
00396 }
00397
00398 TagMatcher* TagMatcher::clone() const
00399 {
00400 return new TagMatcher(*this);
00401 }
00402
00403
00404 TagMatcher::TagMatcher(const TagMatcher& other) : AbstractMatcher(other), d(0)
00405 {
00406 *this = other;
00407 }
00408
00409 void TagMatcher::writeConfig(KConfig* config) const
00410 {
00411 config->writeEntry(QString::fromLatin1("matcherType"), QString::fromLatin1("TagMatcher"));
00412 config->writeEntry(QString::fromLatin1("matcherParams"), d->tagID);
00413 }
00414
00415 void TagMatcher::readConfig(KConfig* config)
00416 {
00417 d->tagID = config->readEntry(QString::fromLatin1("matcherParams"));
00418 }
00419
00420 bool TagMatcher::operator==(const AbstractMatcher& other) const
00421 {
00422 AbstractMatcher* ptr = const_cast<AbstractMatcher*>(&other);
00423 TagMatcher* tagFilter = dynamic_cast<TagMatcher*>(ptr);
00424 return tagFilter ? *d == *(tagFilter->d) : false;
00425 }
00426
00427 bool TagMatcher::operator!=(const AbstractMatcher &other) const
00428 {
00429 return !(*this == other);
00430 }
00431
00432 TagMatcher& TagMatcher::operator=(const TagMatcher& other)
00433 {
00434 d = new TagMatcherPrivate;
00435 *d = *(other.d);
00436 return *this;
00437 }
00438
00439 void DeleteAction::exec(Article& article)
00440 {
00441 if (!article.isNull())
00442 article.setDeleted();
00443 }
00444
00445 SetStatusAction::SetStatusAction(int status) : m_status(status)
00446 {
00447 }
00448
00449 void SetStatusAction::exec(Article& article)
00450 {
00451 if (!article.isNull())
00452 article.setStatus(m_status);
00453 }
00454
00455 int SetStatusAction::status() const
00456 {
00457 return m_status;
00458 }
00459
00460 void SetStatusAction::setStatus(int status)
00461 {
00462 m_status = status;
00463 }
00464
00465 void SetStatusAction::writeConfig(KConfig* config) const
00466 {
00467 config->writeEntry(QString::fromLatin1("actionType"), QString::fromLatin1("SetStatusAction"));
00468 config->writeEntry(QString::fromLatin1("actionParams"), m_status);
00469 }
00470
00471 void SetStatusAction::readConfig(KConfig* config)
00472 {
00473 m_status = config->readNumEntry(QString::fromLatin1("actionParams"), Article::Read);
00474 }
00475
00476 bool SetStatusAction::operator==(const AbstractAction& other)
00477 {
00478 AbstractAction* ptr = const_cast<AbstractAction*>(&other);
00479 SetStatusAction* o = dynamic_cast<SetStatusAction*>(ptr);
00480 if (!o)
00481 return false;
00482 else
00483 return m_status == o->m_status;
00484 }
00485
00486
00487 AssignTagAction::AssignTagAction(const QString& tagID) : m_tagID(tagID)
00488 {
00489 }
00490
00491 void AssignTagAction::exec(Article& article)
00492 {
00493 if (!article.isNull())
00494 article.addTag(m_tagID);
00495 }
00496
00497 class ArticleFilter::ArticleFilterPrivate : public Shared
00498 {
00499 public:
00500 AbstractAction* action;
00501 AbstractMatcher* matcher;
00502 QString name;
00503 int id;
00504
00505 };
00506
00507 ArticleFilter::ArticleFilter() : d(new ArticleFilterPrivate)
00508 {
00509 d->id = KApplication::random();
00510 d->action = 0;
00511 d->matcher = 0;
00512 }
00513
00514 ArticleFilter::ArticleFilter(const AbstractMatcher& matcher, const AbstractAction& action) : d(new ArticleFilterPrivate)
00515 {
00516 d->id = KApplication::random();
00517 d->matcher = matcher.clone();
00518 d->action = action.clone();
00519 }
00520
00521 ArticleFilter::ArticleFilter(const ArticleFilter& other)
00522 {
00523 *this = other;
00524 }
00525
00526 ArticleFilter::~ArticleFilter()
00527 {
00528 if (d->deref())
00529 {
00530 delete d->action;
00531 delete d->matcher;
00532 delete d;
00533 d = 0;
00534 }
00535
00536 }
00537
00538 AbstractMatcher* ArticleFilter::matcher() const
00539 {
00540 return d->matcher;
00541 }
00542
00543 AbstractAction* ArticleFilter::action() const
00544 {
00545 return d->action;
00546 }
00547
00548 void ArticleFilter::setMatcher(const AbstractMatcher& matcher)
00549 {
00550 delete d->matcher;
00551 d->matcher = matcher.clone();
00552 }
00553
00554 void ArticleFilter::setAction(const AbstractAction& action)
00555 {
00556 delete d->action;
00557 d->action = action.clone();
00558 }
00559
00560 ArticleFilter& ArticleFilter::operator=(const ArticleFilter& other)
00561 {
00562 if (this != &other)
00563 {
00564 other.d->ref();
00565 if (d && d->deref())
00566 delete d;
00567 d = other.d;
00568 }
00569 return *this;
00570 }
00571
00572 int ArticleFilter::id() const
00573 {
00574 return d->id;
00575 }
00576
00577 bool ArticleFilter::operator==(const ArticleFilter& other) const
00578 {
00579 return *(d->matcher) == *(other.d->matcher) && *(d->action) == *(other.d->action) && d->name == other.d->name;
00580 }
00581
00582 void ArticleFilterList::writeConfig(KConfig* config) const
00583 {
00584 config->setGroup(QString::fromLatin1("Filters"));
00585 config->writeEntry(QString::fromLatin1("count"), count());
00586 int index = 0;
00587 for (ArticleFilterList::ConstIterator it = begin(); it != end(); ++it)
00588 {
00589 config->setGroup(QString::fromLatin1("Filters_")+QString::number(index));
00590 (*it).writeConfig(config);
00591 ++index;
00592 }
00593 }
00594
00595 void ArticleFilterList::readConfig(KConfig* config)
00596 {
00597 clear();
00598 config->setGroup(QString::fromLatin1("Filters"));
00599 int count = config->readNumEntry(QString::fromLatin1("count"), 0);
00600 for (int i = 0; i < count; ++i)
00601 {
00602 config->setGroup(QString::fromLatin1("Filters_")+QString::number(i));
00603 ArticleFilter filter;
00604 filter.readConfig(config);
00605 append(filter);
00606 }
00607 }
00608
00609
00610 void AssignTagAction::readConfig(KConfig* config)
00611 {
00612 m_tagID = config->readEntry(QString::fromLatin1("actionParams"));
00613 }
00614
00615 void AssignTagAction::writeConfig(KConfig* config) const
00616 {
00617 config->writeEntry(QString::fromLatin1("actionType"), QString::fromLatin1("AssignTagAction"));
00618 config->writeEntry(QString::fromLatin1("actionParams"), m_tagID);
00619 }
00620
00621 bool AssignTagAction::operator==(const AbstractAction& other)
00622 {
00623 AbstractAction* ptr = const_cast<AbstractAction*>(&other);
00624 AssignTagAction* o = dynamic_cast<AssignTagAction*>(ptr);
00625 if (!o)
00626 return false;
00627 else
00628 return m_tagID == o->m_tagID;
00629 }
00630
00631 const QString& AssignTagAction::tagID() const
00632 {
00633 return m_tagID;
00634 }
00635
00636 void AssignTagAction::setTagID(const QString& tagID)
00637 {
00638 m_tagID = tagID;
00639 }
00640
00641 void DeleteAction::readConfig(KConfig* )
00642 {
00643 }
00644
00645 void DeleteAction::writeConfig(KConfig* config) const
00646 {
00647 config->writeEntry(QString::fromLatin1("actionType"), QString::fromLatin1("DeleteAction"));
00648 }
00649
00650 bool DeleteAction::operator==(const AbstractAction& other)
00651 {
00652 AbstractAction* ptr = const_cast<AbstractAction*>(&other);
00653 DeleteAction* o = dynamic_cast<DeleteAction*>(ptr);
00654 return o != 0;
00655 }
00656
00657 void ArticleFilter::readConfig(KConfig* config)
00658 {
00659 delete d->matcher;
00660 d->matcher = 0;
00661 delete d->action;
00662 d->action = 0;
00663
00664 d->name = config->readEntry(QString::fromLatin1("name"));
00665 d->id = config->readNumEntry(QString::fromLatin1("id"), 0);
00666
00667 QString matcherType = config->readEntry(QString::fromLatin1("matcherType"));
00668
00669 if (matcherType == QString::fromLatin1("TagMatcher"))
00670 d->matcher = new TagMatcher();
00671 else if (matcherType == QString::fromLatin1("ArticleMatcher"))
00672 d->matcher = new ArticleMatcher();
00673
00674 if (d->matcher)
00675 d->matcher->readConfig(config);
00676
00677
00678 QString actionType = config->readEntry(QString::fromLatin1("actionType"));
00679
00680 if (actionType == QString::fromLatin1("AssignTagAction"))
00681 d->action = new AssignTagAction();
00682 else if (actionType == QString::fromLatin1("DeleteAction"))
00683 d->action = new DeleteAction();
00684 else if (actionType == QString::fromLatin1("SetStatusAction"))
00685 d->action = new SetStatusAction();
00686
00687 if (d->action)
00688 d->action->readConfig(config);
00689 }
00690
00691 void ArticleFilter::writeConfig(KConfig* config) const
00692 {
00693 config->writeEntry(QString::fromLatin1("name"), d->name);
00694 config->writeEntry(QString::fromLatin1("id"), d->id);
00695 d->matcher->writeConfig(config);
00696 d->action->writeConfig(config);
00697 }
00698
00699 void ArticleFilter::setName(const QString& name)
00700 {
00701 d->name = name;
00702 }
00703
00704 const QString& ArticleFilter::name() const
00705 {
00706 return d->name;
00707 }
00708
00709 void ArticleFilter::applyTo(Article& article) const
00710 {
00711 if (d->matcher && d->action && d->matcher->matches(article))
00712 d->action->exec(article);
00713 }
00714 }
00715 }
|