From da0c37dbad121252effa85941de4d49222176179 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 20 Oct 2016 13:34:38 +0200 Subject: A new indexer subsystem that can be used for indexes that are more complex than a simple key-value pair. --- common/mail/threadindexer.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++ common/mail/threadindexer.h | 36 +++++++++++ 2 files changed, 176 insertions(+) create mode 100644 common/mail/threadindexer.cpp create mode 100644 common/mail/threadindexer.h (limited to 'common/mail') diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp new file mode 100644 index 0000000..4a18625 --- /dev/null +++ b/common/mail/threadindexer.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Christian Mollekopf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "threadindexer.h" + +#include "typeindex.h" +#include "log.h" + +SINK_DEBUG_AREA("threadindex") + +using namespace Sink; +using namespace Sink::ApplicationDomain; + +static QString stripOffPrefixes(const QString &subject) +{ + //TODO this hardcoded list is probably not good enough (especially regarding internationalization) + //TODO this whole routine, including internationalized re/fwd ... should go into some library. + //We'll require the same for generating reply/forward subjects in kube + static QStringList defaultReplyPrefixes = QStringList() << QLatin1String("Re\\s*:") + << QLatin1String("Re\\[\\d+\\]:") + << QLatin1String("Re\\d+:"); + + static QStringList defaultForwardPrefixes = QStringList() << QLatin1String("Fwd:") + << QLatin1String("FW:"); + + QStringList replyPrefixes; // = GlobalSettings::self()->replyPrefixes(); + if (replyPrefixes.isEmpty()) { + replyPrefixes = defaultReplyPrefixes; + } + + QStringList forwardPrefixes; // = GlobalSettings::self()->forwardPrefixes(); + if (forwardPrefixes.isEmpty()) { + forwardPrefixes = defaultReplyPrefixes; + } + + const QStringList prefixRegExps = replyPrefixes + forwardPrefixes; + + // construct a big regexp that + // 1. is anchored to the beginning of str (sans whitespace) + // 2. matches at least one of the part regexps in prefixRegExps + const QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QLatin1String(")|(?:"))); + + static QString regExpPattern; + static QRegExp regExp; + + regExp.setCaseSensitivity(Qt::CaseInsensitive); + if (regExpPattern != bigRegExp) { + // the prefixes have changed, so update the regexp + regExpPattern = bigRegExp; + regExp.setPattern(regExpPattern); + } + + if(regExp.isValid()) { + QString tmp = subject; + if (regExp.indexIn( tmp ) == 0) { + return tmp.remove(0, regExp.matchedLength()); + } + } else { + SinkWarning() << "bigRegExp = \"" + << bigRegExp << "\"\n" + << "prefix regexp is invalid!"; + } + + return subject; +} + + +void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) +{ + auto messageId = entity.getProperty(Mail::MessageId::name); + auto parentMessageId = entity.getProperty(Mail::ParentMessageId::name); + auto subject = entity.getProperty(Mail::Subject::name); + + auto normalizedSubject = stripOffPrefixes(subject.toString()).toUtf8(); + + QVector thread; + + //a child already registered our thread. + thread = index().secondaryLookup(messageId, transaction); + + //If parent is already available, add to thread of parent + if (thread.isEmpty() && parentMessageId.isValid()) { + thread = index().secondaryLookup(parentMessageId, transaction); + SinkTrace() << "Found parent"; + } + if (thread.isEmpty()) { + //Try to lookup the thread by subject: + thread = index().secondaryLookup(normalizedSubject, transaction); + if (thread.isEmpty()) { + SinkTrace() << "Created a new thread "; + thread << QUuid::createUuid().toByteArray(); + } else { + } + } + + //We should have found the thread by now + if (!thread.isEmpty()) { + if (parentMessageId.isValid()) { + //Register parent with thread for when it becomes available + index().index(parentMessageId, thread.first(), transaction); + } + index().index(messageId, thread.first(), transaction); + index().index(thread.first(), messageId, transaction); + index().index(normalizedSubject, thread.first(), transaction); + } else { + SinkWarning() << "Couldn't find a thread for: " << messageId; + } +} + + +void ThreadIndexer::add(const ApplicationDomain::ApplicationDomainType &entity) +{ + updateThreadingIndex(entity.identifier(), entity, transaction()); +} + +void ThreadIndexer::modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) +{ + +} + +void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) +{ + +} + diff --git a/common/mail/threadindexer.h b/common/mail/threadindexer.h new file mode 100644 index 0000000..064ae71 --- /dev/null +++ b/common/mail/threadindexer.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Christian Mollekopf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include "indexer.h" + +namespace Sink { + +class ThreadIndexer : public Indexer +{ +public: + typedef QSharedPointer Ptr; + virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; + virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; + virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; +private: + void updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); +}; + +} -- cgit v1.2.3