summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/CMakeLists.txt2
-rw-r--r--common/domain/mail.cpp99
-rw-r--r--common/indexer.cpp39
-rw-r--r--common/indexer.h49
-rw-r--r--common/mail/threadindexer.cpp140
-rw-r--r--common/mail/threadindexer.h36
-rw-r--r--common/typeindex.cpp8
-rw-r--r--common/typeindex.h9
8 files changed, 286 insertions, 96 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index e329d93..b5efb62 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -75,6 +75,8 @@ set(command_SRCS
75 specialpurposepreprocessor.cpp 75 specialpurposepreprocessor.cpp
76 datastorequery.cpp 76 datastorequery.cpp
77 storage/entitystore.cpp 77 storage/entitystore.cpp
78 indexer.cpp
79 mail/threadindexer.cpp
78 ${storage_SRCS}) 80 ${storage_SRCS})
79 81
80add_library(${PROJECT_NAME} SHARED ${command_SRCS}) 82add_library(${PROJECT_NAME} SHARED ${command_SRCS})
diff --git a/common/domain/mail.cpp b/common/domain/mail.cpp
index 9d58767..4b33f06 100644
--- a/common/domain/mail.cpp
+++ b/common/domain/mail.cpp
@@ -35,6 +35,7 @@
35#include "entitybuffer.h" 35#include "entitybuffer.h"
36#include "datastorequery.h" 36#include "datastorequery.h"
37#include "entity_generated.h" 37#include "entity_generated.h"
38#include "mail/threadindexer.h"
38 39
39#include "mail_generated.h" 40#include "mail_generated.h"
40 41
@@ -59,6 +60,8 @@ void TypeImplementation<Mail>::configureIndex(TypeIndex &index)
59 index.addProperty<QByteArray>(Mail::ParentMessageId::name); 60 index.addProperty<QByteArray>(Mail::ParentMessageId::name);
60 61
61 index.addProperty<Mail::MessageId>(); 62 index.addProperty<Mail::MessageId>();
63
64 index.addSecondaryPropertyIndexer<Mail::MessageId, Mail::ThreadId, ThreadIndexer>();
62 index.addSecondaryProperty<Mail::MessageId, Mail::ThreadId>(); 65 index.addSecondaryProperty<Mail::MessageId, Mail::ThreadId>();
63 index.addSecondaryProperty<Mail::ThreadId, Mail::MessageId>(); 66 index.addSecondaryProperty<Mail::ThreadId, Mail::MessageId>();
64} 67}
@@ -74,102 +77,6 @@ static TypeIndex &getIndex()
74 return *index; 77 return *index;
75} 78}
76 79
77static QString stripOffPrefixes(const QString &subject)
78{
79 //TODO this hardcoded list is probably not good enough (especially regarding internationalization)
80 //TODO this whole routine, including internationalized re/fwd ... should go into some library.
81 //We'll require the same for generating reply/forward subjects in kube
82 static QStringList defaultReplyPrefixes = QStringList() << QLatin1String("Re\\s*:")
83 << QLatin1String("Re\\[\\d+\\]:")
84 << QLatin1String("Re\\d+:");
85
86 static QStringList defaultForwardPrefixes = QStringList() << QLatin1String("Fwd:")
87 << QLatin1String("FW:");
88
89 QStringList replyPrefixes; // = GlobalSettings::self()->replyPrefixes();
90 if (replyPrefixes.isEmpty()) {
91 replyPrefixes = defaultReplyPrefixes;
92 }
93
94 QStringList forwardPrefixes; // = GlobalSettings::self()->forwardPrefixes();
95 if (forwardPrefixes.isEmpty()) {
96 forwardPrefixes = defaultReplyPrefixes;
97 }
98
99 const QStringList prefixRegExps = replyPrefixes + forwardPrefixes;
100
101 // construct a big regexp that
102 // 1. is anchored to the beginning of str (sans whitespace)
103 // 2. matches at least one of the part regexps in prefixRegExps
104 const QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QLatin1String(")|(?:")));
105
106 static QString regExpPattern;
107 static QRegExp regExp;
108
109 regExp.setCaseSensitivity(Qt::CaseInsensitive);
110 if (regExpPattern != bigRegExp) {
111 // the prefixes have changed, so update the regexp
112 regExpPattern = bigRegExp;
113 regExp.setPattern(regExpPattern);
114 }
115
116 if(regExp.isValid()) {
117 QString tmp = subject;
118 if (regExp.indexIn( tmp ) == 0) {
119 return tmp.remove(0, regExp.matchedLength());
120 }
121 } else {
122 SinkWarning() << "bigRegExp = \""
123 << bigRegExp << "\"\n"
124 << "prefix regexp is invalid!";
125 }
126
127 return subject;
128}
129
130
131static void updateThreadingIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::DataStore::Transaction &transaction)
132{
133 auto messageId = bufferAdaptor.getProperty(Mail::MessageId::name);
134 auto parentMessageId = bufferAdaptor.getProperty(Mail::ParentMessageId::name);
135 auto subject = bufferAdaptor.getProperty(Mail::Subject::name);
136
137 auto normalizedSubject = stripOffPrefixes(subject.toString()).toUtf8();
138
139 QVector<QByteArray> thread;
140
141 //a child already registered our thread.
142 thread = getIndex().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId, transaction);
143
144 //If parent is already available, add to thread of parent
145 if (thread.isEmpty() && parentMessageId.isValid()) {
146 thread = getIndex().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId, transaction);
147 SinkTrace() << "Found parent";
148 }
149 if (thread.isEmpty()) {
150 //Try to lookup the thread by subject:
151 thread = getIndex().secondaryLookup<Mail::Subject, Mail::ThreadId>(normalizedSubject, transaction);
152 if (thread.isEmpty()) {
153 SinkTrace() << "Created a new thread ";
154 thread << QUuid::createUuid().toByteArray();
155 } else {
156 }
157 }
158
159 //We should have found the thread by now
160 if (!thread.isEmpty()) {
161 if (parentMessageId.isValid()) {
162 //Register parent with thread for when it becomes available
163 getIndex().index<Mail::MessageId, Mail::ThreadId>(parentMessageId, thread.first(), transaction);
164 }
165 getIndex().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction);
166 getIndex().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction);
167 getIndex().index<Mail::Subject, Mail::ThreadId>(normalizedSubject, thread.first(), transaction);
168 } else {
169 SinkWarning() << "Couldn't find a thread for: " << messageId;
170 }
171}
172
173QSharedPointer<ReadPropertyMapper<TypeImplementation<Mail>::Buffer> > TypeImplementation<Mail>::initializeReadPropertyMapper() 80QSharedPointer<ReadPropertyMapper<TypeImplementation<Mail>::Buffer> > TypeImplementation<Mail>::initializeReadPropertyMapper()
174{ 81{
175 auto propertyMapper = QSharedPointer<ReadPropertyMapper<Buffer> >::create(); 82 auto propertyMapper = QSharedPointer<ReadPropertyMapper<Buffer> >::create();
diff --git a/common/indexer.cpp b/common/indexer.cpp
new file mode 100644
index 0000000..1b223b3
--- /dev/null
+++ b/common/indexer.cpp
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#include "indexer.h"
20
21using namespace Sink;
22
23void Indexer::setup(TypeIndex *index, Storage::DataStore::Transaction *transaction)
24{
25 mTypeIndex = index;
26 mTransaction = transaction;
27}
28
29Storage::DataStore::Transaction &Indexer::transaction()
30{
31 Q_ASSERT(mTransaction);
32 return *mTransaction;
33}
34
35TypeIndex &Indexer::index()
36{
37 Q_ASSERT(mTypeIndex);
38 return *mTypeIndex;
39}
diff --git a/common/indexer.h b/common/indexer.h
new file mode 100644
index 0000000..7e148d1
--- /dev/null
+++ b/common/indexer.h
@@ -0,0 +1,49 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include "storage.h"
22#include <QSharedPointer>
23
24class TypeIndex;
25namespace Sink {
26namespace ApplicationDomain {
27 class ApplicationDomainType;
28}
29
30class Indexer
31{
32public:
33 typedef QSharedPointer<Indexer> Ptr;
34 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) = 0;
35 virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) = 0;
36 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) = 0;
37
38protected:
39 Storage::DataStore::Transaction &transaction();
40 TypeIndex &index();
41
42private:
43 friend class ::TypeIndex;
44 void setup(TypeIndex *, Storage::DataStore::Transaction *);
45 Storage::DataStore::Transaction *mTransaction;
46 TypeIndex *mTypeIndex;
47};
48
49}
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 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#include "threadindexer.h"
20
21#include "typeindex.h"
22#include "log.h"
23
24SINK_DEBUG_AREA("threadindex")
25
26using namespace Sink;
27using namespace Sink::ApplicationDomain;
28
29static QString stripOffPrefixes(const QString &subject)
30{
31 //TODO this hardcoded list is probably not good enough (especially regarding internationalization)
32 //TODO this whole routine, including internationalized re/fwd ... should go into some library.
33 //We'll require the same for generating reply/forward subjects in kube
34 static QStringList defaultReplyPrefixes = QStringList() << QLatin1String("Re\\s*:")
35 << QLatin1String("Re\\[\\d+\\]:")
36 << QLatin1String("Re\\d+:");
37
38 static QStringList defaultForwardPrefixes = QStringList() << QLatin1String("Fwd:")
39 << QLatin1String("FW:");
40
41 QStringList replyPrefixes; // = GlobalSettings::self()->replyPrefixes();
42 if (replyPrefixes.isEmpty()) {
43 replyPrefixes = defaultReplyPrefixes;
44 }
45
46 QStringList forwardPrefixes; // = GlobalSettings::self()->forwardPrefixes();
47 if (forwardPrefixes.isEmpty()) {
48 forwardPrefixes = defaultReplyPrefixes;
49 }
50
51 const QStringList prefixRegExps = replyPrefixes + forwardPrefixes;
52
53 // construct a big regexp that
54 // 1. is anchored to the beginning of str (sans whitespace)
55 // 2. matches at least one of the part regexps in prefixRegExps
56 const QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QLatin1String(")|(?:")));
57
58 static QString regExpPattern;
59 static QRegExp regExp;
60
61 regExp.setCaseSensitivity(Qt::CaseInsensitive);
62 if (regExpPattern != bigRegExp) {
63 // the prefixes have changed, so update the regexp
64 regExpPattern = bigRegExp;
65 regExp.setPattern(regExpPattern);
66 }
67
68 if(regExp.isValid()) {
69 QString tmp = subject;
70 if (regExp.indexIn( tmp ) == 0) {
71 return tmp.remove(0, regExp.matchedLength());
72 }
73 } else {
74 SinkWarning() << "bigRegExp = \""
75 << bigRegExp << "\"\n"
76 << "prefix regexp is invalid!";
77 }
78
79 return subject;
80}
81
82
83void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction)
84{
85 auto messageId = entity.getProperty(Mail::MessageId::name);
86 auto parentMessageId = entity.getProperty(Mail::ParentMessageId::name);
87 auto subject = entity.getProperty(Mail::Subject::name);
88
89 auto normalizedSubject = stripOffPrefixes(subject.toString()).toUtf8();
90
91 QVector<QByteArray> thread;
92
93 //a child already registered our thread.
94 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId, transaction);
95
96 //If parent is already available, add to thread of parent
97 if (thread.isEmpty() && parentMessageId.isValid()) {
98 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId, transaction);
99 SinkTrace() << "Found parent";
100 }
101 if (thread.isEmpty()) {
102 //Try to lookup the thread by subject:
103 thread = index().secondaryLookup<Mail::Subject, Mail::ThreadId>(normalizedSubject, transaction);
104 if (thread.isEmpty()) {
105 SinkTrace() << "Created a new thread ";
106 thread << QUuid::createUuid().toByteArray();
107 } else {
108 }
109 }
110
111 //We should have found the thread by now
112 if (!thread.isEmpty()) {
113 if (parentMessageId.isValid()) {
114 //Register parent with thread for when it becomes available
115 index().index<Mail::MessageId, Mail::ThreadId>(parentMessageId, thread.first(), transaction);
116 }
117 index().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction);
118 index().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction);
119 index().index<Mail::Subject, Mail::ThreadId>(normalizedSubject, thread.first(), transaction);
120 } else {
121 SinkWarning() << "Couldn't find a thread for: " << messageId;
122 }
123}
124
125
126void ThreadIndexer::add(const ApplicationDomain::ApplicationDomainType &entity)
127{
128 updateThreadingIndex(entity.identifier(), entity, transaction());
129}
130
131void ThreadIndexer::modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity)
132{
133
134}
135
136void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity)
137{
138
139}
140
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 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include "indexer.h"
22
23namespace Sink {
24
25class ThreadIndexer : public Indexer
26{
27public:
28 typedef QSharedPointer<ThreadIndexer> Ptr;
29 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
30 virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
31 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
32private:
33 void updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction);
34};
35
36}
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index 7920efc..8f5de4f 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -121,6 +121,10 @@ void TypeIndex::add(const QByteArray &identifier, const Sink::ApplicationDomain:
121 auto indexer = mSortIndexer.value(it.key() + it.value()); 121 auto indexer = mSortIndexer.value(it.key() + it.value());
122 indexer(identifier, value, sortValue, transaction); 122 indexer(identifier, value, sortValue, transaction);
123 } 123 }
124 for (const auto &indexer : mCustomIndexer) {
125 indexer->setup(this, &transaction);
126 indexer->add(entity);
127 }
124} 128}
125 129
126void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) 130void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction)
@@ -138,6 +142,10 @@ void TypeIndex::remove(const QByteArray &identifier, const Sink::ApplicationDoma
138 Index(indexName(it.key(), it.value()), transaction).remove(propertyValue.toByteArray() + sortValue.toByteArray(), identifier); 142 Index(indexName(it.key(), it.value()), transaction).remove(propertyValue.toByteArray() + sortValue.toByteArray(), identifier);
139 } 143 }
140 } 144 }
145 for (const auto &indexer : mCustomIndexer) {
146 indexer->setup(this, &transaction);
147 indexer->remove(entity);
148 }
141} 149}
142 150
143static QVector<QByteArray> indexLookup(Index &index, Query::Comparator filter) 151static QVector<QByteArray> indexLookup(Index &index, Query::Comparator filter)
diff --git a/common/typeindex.h b/common/typeindex.h
index e11e673..041e04a 100644
--- a/common/typeindex.h
+++ b/common/typeindex.h
@@ -22,6 +22,7 @@
22#include "storage.h" 22#include "storage.h"
23#include "query.h" 23#include "query.h"
24#include "log.h" 24#include "log.h"
25#include "indexer.h"
25#include <QByteArray> 26#include <QByteArray>
26 27
27class TypeIndex 28class TypeIndex
@@ -51,6 +52,13 @@ public:
51 { 52 {
52 mSecondaryProperties.insert(Left::name, Right::name); 53 mSecondaryProperties.insert(Left::name, Right::name);
53 } 54 }
55
56 template <typename Left, typename Right, typename CustomIndexer>
57 void addSecondaryPropertyIndexer()
58 {
59 mCustomIndexer << CustomIndexer::Ptr::create();
60 }
61
54 void add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); 62 void add(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction);
55 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); 63 void remove(const QByteArray &identifier, const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction);
56 64
@@ -84,6 +92,7 @@ private:
84 QMap<QByteArray, QByteArray> mSortedProperties; 92 QMap<QByteArray, QByteArray> mSortedProperties;
85 //<Property, ResultProperty> 93 //<Property, ResultProperty>
86 QMap<QByteArray, QByteArray> mSecondaryProperties; 94 QMap<QByteArray, QByteArray> mSecondaryProperties;
95 QList<Sink::Indexer::Ptr> mCustomIndexer;
87 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mIndexer; 96 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction)>> mIndexer;
88 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction)>> mSortIndexer; 97 QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::DataStore::Transaction &transaction)>> mSortIndexer;
89}; 98};