Skip to content

Commit

Permalink
[qtcontacts-sqlite] Automatically extend known display label groups a…
Browse files Browse the repository at this point in the history
…t runtime. Contributes to JB#44742
  • Loading branch information
Chris Adams committed Apr 15, 2019
1 parent 905f6b7 commit 787f62e
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 64 deletions.
118 changes: 104 additions & 14 deletions src/engine/contactsdatabase.cpp
Expand Up @@ -1898,7 +1898,7 @@ static bool executeDisplayLabelGroupLocalizationStatements(QSqlDatabase &databas

const QString dlg = cdb->determineDisplayLabelGroup(c);
displayLabelGroups.append(dlg);
displayLabelGroupSortOrders.append(cdb->possibleDisplayLabelGroups().indexOf(dlg) + 1);
displayLabelGroupSortOrders.append(cdb->displayLabelGroupSortValue(dlg));
}
selectQuery.finish();
}
Expand Down Expand Up @@ -2660,6 +2660,60 @@ static QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> initiali
}
static QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> s_dlgGenerators = initializeDisplayLabelGroupGenerators();

static qint32 displayLabelGroupSortValue(const QString &group, const QMap<QString, int> &knownDisplayLabelGroups)
{
static const int maxUnicodeCodePointValue = 1114111; // 0x10FFFF
static const int numberGroupSortValue = maxUnicodeCodePointValue + 1;

qint32 retn = -1;
if (!group.isEmpty()) {
retn = group == QStringLiteral("#") ? numberGroupSortValue : knownDisplayLabelGroups.value(group, -1);
if (retn < 0) {
// the group is not a previously-known display label group.
// convert the group to a utf32 code point value.
const QChar first = group.at(0);
if (first.isSurrogate()) {
if (group.size() >= 2) {
const QChar second = group.at(1);
retn = ((first.isHighSurrogate() ? first.unicode() : second.unicode() - 0xD800) * 0x400)
+ (second.isLowSurrogate() ? second.unicode() : first.unicode() - 0xDC00) + 0x10000;
} else {
// cannot calculate the true codepoint without the second character in the surrogate pair.
// assume that it's the very last possible codepoint.
retn = maxUnicodeCodePointValue;
}
} else {
// use the unicode code point value as the sort value.
retn = first.unicode();

// resolve overlap issue by compressing overlapping groups
// into a single subsequent group.
// e.g. in Chinese locale, there may be more than
// 65 default display label groups, and thus the
// letter 'A' (whose unicode value is 65) would overlap.
int lastContiguousSortValue = -1;
for (int sortValue : knownDisplayLabelGroups) {
if (sortValue != (lastContiguousSortValue + 1)) {
break;
}
lastContiguousSortValue = sortValue;
}

// instead of placing into LCSV+1, we place into LCSV+2
// to ensure that ALL overlapping groups are compressed
// into the same group, in order to avoid "first seen
// will sort first" issues (e.g. B < A).
const int compressedSortValue = lastContiguousSortValue + 2;
if (retn < compressedSortValue) {
retn = compressedSortValue;
}
}
}
}

return retn;
}

// Adapted from the inter-process mutex in QMF
// The first user creates the semaphore that all subsequent instances
// attach to. We rely on undo semantics to release locked semaphores
Expand Down Expand Up @@ -2721,8 +2775,9 @@ void ContactsDatabase::Query::reportError(const char *text) const
reportError(QString::fromLatin1(text));
}

ContactsDatabase::ContactsDatabase()
: m_mutex(QMutex::Recursive)
ContactsDatabase::ContactsDatabase(ContactsEngine *engine)
: m_engine(engine)
, m_mutex(QMutex::Recursive)
, m_nonprivileged(false)
, m_localeName(QLocale().name())
, m_defaultGenerator(new DefaultDlgGenerator)
Expand Down Expand Up @@ -2768,21 +2823,38 @@ bool ContactsDatabase::open(const QString &connectionName, bool nonprivileged, b
}
m_dlgGenerators.append(m_defaultGenerator.data());

// and build a "superlist" of possible display label groups
// which defines a total sort ordering for display label groups.
// and build a "superlist" of known display label groups.
const QLocale locale;
QStringList knownDisplayLabelGroups;
for (auto generator : m_dlgGenerators) {
if (generator->validForLocale(locale)) {
const QStringList groups = generator->displayLabelGroups();
for (const QString &group : groups) {
if (!m_possibleDisplayLabelGroups.contains(group)) {
m_possibleDisplayLabelGroups.append(group);
if (!knownDisplayLabelGroups.contains(group)) {
knownDisplayLabelGroups.append(group);
}
}
}
}
m_possibleDisplayLabelGroups.removeAll(QStringLiteral("#"));
m_possibleDisplayLabelGroups.append(QStringLiteral("#"));
knownDisplayLabelGroups.removeAll(QStringLiteral("#"));
knownDisplayLabelGroups.append(QStringLiteral("#"));

// from that list, build a mapping from group to sort priority value,
// based upon the position of each group in the list,
// which defines a total sort ordering for known display label groups.
for (int i = 0; i < knownDisplayLabelGroups.size(); ++i) {
const QString &group(knownDisplayLabelGroups.at(i));
m_knownDisplayLabelGroupsSortValues.insert(
group,
group == QStringLiteral("#")
? ::displayLabelGroupSortValue(group, m_knownDisplayLabelGroupsSortValues)
: i);
}

// XXX TODO: do we need to add groups which currently exist in the database,
// but which aren't currently included in the m_knownDisplayLabelGroupsSortValues?
// I don't think we do, since it only matters on write, and we will update
// the m_knownDisplayLabelGroupsSortValues in determineDisplayLabelGroup() during write...
}

if (m_database.isOpen()) {
Expand Down Expand Up @@ -3263,11 +3335,12 @@ QDateTime ContactsDatabase::fromDateTimeString(const QString &s)
return QDateTime(datepart, timepart, Qt::UTC);
}

QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c) const
QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c)
{
// TODO: read the preferred detail and field from system settings!
// XXXXXXXX TODO: read the preferred detail and field from system settings!
const int preferredDetail = QContactName::Type;
const int preferredField = QContactName::FieldLastName;

QString data;
if (preferredDetail == QContactName::Type) {
// try to use the preferred field data.
Expand Down Expand Up @@ -3315,6 +3388,21 @@ QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c) const
}
}

if (!group.isEmpty() && !m_knownDisplayLabelGroupsSortValues.contains(group)) {
// We are about to write a contact to the database which has a
// display label group which previously was not known / observed.
// Calculate the sort value for the display label group,
// and add it to our map of displayLabelGroup->sortValue.
// Note: this should be thread-safe since we only call this method within writes.
m_knownDisplayLabelGroupsSortValues.insert(
group, ::displayLabelGroupSortValue(
group,
m_knownDisplayLabelGroupsSortValues));

// and invoke engine->_q_displayLabelGroupsChanged() asynchronously.
QMetaObject::invokeMethod(m_engine, "_q_displayLabelGroupsChanged", Qt::QueuedConnection);
}

return group;
}

Expand All @@ -3337,7 +3425,7 @@ QStringList ContactsDatabase::displayLabelGroups() const
QMutexLocker locker(accessMutex());
QSqlQuery selectQuery(m_database);
selectQuery.setForwardOnly(true);
const QString statement = QStringLiteral("SELECT DISTINCT DisplayLabelGroup FROM Contacts");
const QString statement = QStringLiteral("SELECT DISTINCT DisplayLabelGroup FROM Contacts ORDER BY DisplayLabelGroupSortOrder ASC");
if (!selectQuery.prepare(statement)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to prepare distinct display label group selection query: %1\n%2")
.arg(selectQuery.lastError().text())
Expand All @@ -3363,9 +3451,11 @@ QStringList ContactsDatabase::displayLabelGroups() const
return groups;
}

QStringList ContactsDatabase::possibleDisplayLabelGroups() const
int ContactsDatabase::displayLabelGroupSortValue(const QString &group) const
{
return m_possibleDisplayLabelGroups;
static const int maxUnicodeCodePointValue = 1114111; // 0x10FFFF
static const int nullGroupSortValue = maxUnicodeCodePointValue + 1;
return m_knownDisplayLabelGroupsSortValues.value(group, nullGroupSortValue);
}

#include "../extensions/qcontactdeactivated_impl.h"
Expand Down
10 changes: 6 additions & 4 deletions src/engine/contactsdatabase.h
Expand Up @@ -47,6 +47,7 @@

#include <QContact>

class ContactsEngine;
class ContactsDatabase
{
public:
Expand Down Expand Up @@ -105,7 +106,7 @@ class ContactsDatabase
void reportError(const char *text) const;
};

ContactsDatabase();
ContactsDatabase(ContactsEngine *engine);
~ContactsDatabase();

QMutex *accessMutex() const;
Expand Down Expand Up @@ -152,9 +153,9 @@ class ContactsDatabase
bool removeTransientDetails(quint32 contactId);
bool removeTransientDetails(const QList<quint32> &contactIds);

QString determineDisplayLabelGroup(const QContact &c) const;
QString determineDisplayLabelGroup(const QContact &c);
QStringList displayLabelGroups() const;
QStringList possibleDisplayLabelGroups() const;
int displayLabelGroupSortValue(const QString &group) const;

static bool execute(QSqlQuery &query);
static bool executeBatch(QSqlQuery &query, QSqlQuery::BatchExecutionMode mode = QSqlQuery::ValuesAsRows);
Expand All @@ -171,6 +172,7 @@ class ContactsDatabase
static QDateTime fromDateTimeString(const QString &s);

private:
ContactsEngine *m_engine;
QSqlDatabase m_database;
ContactsTransientStore m_transientStore;
QMutex m_mutex;
Expand All @@ -180,7 +182,7 @@ class ContactsDatabase
QHash<QString, QSqlQuery> m_preparedQueries;
QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> m_dlgGenerators;
QScopedPointer<QtContactsSqliteExtensions::DisplayLabelGroupGenerator> m_defaultGenerator;
QStringList m_possibleDisplayLabelGroups;
QMap<QString, int> m_knownDisplayLabelGroupsSortValues;
};

#endif
13 changes: 7 additions & 6 deletions src/engine/contactsengine.cpp
Expand Up @@ -69,13 +69,13 @@ class Job
{
public:
struct WriterProxy {
const ContactsEngine &engine;
ContactsEngine &engine;
ContactsDatabase &database;
ContactNotifier &notifier;
ContactReader &reader;
mutable ContactWriter *writer;

WriterProxy(const ContactsEngine &e, ContactsDatabase &db, ContactNotifier &n, ContactReader &r)
WriterProxy(ContactsEngine &e, ContactsDatabase &db, ContactNotifier &n, ContactReader &r)
: engine(e), database(db), notifier(n), reader(r), writer(0)
{
}
Expand Down Expand Up @@ -518,6 +518,7 @@ class JobThread : public QThread
JobThread(ContactsEngine *engine, const QString &databaseUuid, bool nonprivileged, bool autoTest)
: m_currentJob(0)
, m_engine(engine)
, m_database(engine)
, m_databaseUuid(databaseUuid)
, m_updatePending(false)
, m_running(false)
Expand Down Expand Up @@ -1174,7 +1175,7 @@ QList<QContactType::TypeValues> ContactsEngine::supportedContactTypes() const
return QList<QContactType::TypeValues>() << QContactType::TypeContact;
}

void ContactsEngine::regenerateDisplayLabel(QContact &contact) const
void ContactsEngine::regenerateDisplayLabel(QContact &contact)
{
QContactManager::Error displayLabelError = QContactManager::NoError;
const QString label = synthesizedDisplayLabel(contact, &displayLabelError);
Expand Down Expand Up @@ -1412,9 +1413,9 @@ void ContactsEngine::_q_syncContactsChanged(const QStringList &syncTargets)
emit syncContactsChanged(syncTargets);
}

void ContactsEngine::_q_displayLabelGroupsChanged(const QStringList &groups)
void ContactsEngine::_q_displayLabelGroupsChanged()
{
emit displayLabelGroupsChanged(groups);
emit displayLabelGroupsChanged(displayLabelGroups());
}

void ContactsEngine::_q_contactsRemoved(const QVector<quint32> &contactIds)
Expand Down Expand Up @@ -1443,7 +1444,7 @@ ContactsDatabase &ContactsEngine::database()
QString dbId(QStringLiteral("qtcontacts-sqlite%1-%2"));
dbId = dbId.arg(m_autoTest ? QStringLiteral("-test") : QString()).arg(databaseUuid());

m_database.reset(new ContactsDatabase);
m_database.reset(new ContactsDatabase(this));
if (!m_database->open(dbId, m_nonprivileged, m_autoTest, true)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Unable to open synchronous engine database connection"));
}
Expand Down
4 changes: 2 additions & 2 deletions src/engine/contactsengine.h
Expand Up @@ -132,7 +132,7 @@ class ContactsEngine : public QtContactsSqliteExtensions::ContactManagerEngine
bool isRelationshipTypeSupported(const QString &relationshipType, QContactType::TypeValues contactType) const override;
QList<QContactType::TypeValues> supportedContactTypes() const override;

void regenerateDisplayLabel(QContact &contact) const;
void regenerateDisplayLabel(QContact &contact);

bool fetchSyncContacts(const QString &syncTarget, const QDateTime &lastSync, const QList<QContactId> &exportedIds,
QList<QContact> *syncContacts, QList<QContact> *addedContacts, QList<QContactId> *deletedContactIds,
Expand Down Expand Up @@ -176,7 +176,7 @@ private slots:
void _q_selfContactIdChanged(quint32,quint32);
void _q_relationshipsAdded(const QVector<quint32> &contactIds);
void _q_relationshipsRemoved(const QVector<quint32> &contactIds);
void _q_displayLabelGroupsChanged(const QStringList &groups);
void _q_displayLabelGroupsChanged();

private:
QString databaseUuid();
Expand Down
4 changes: 2 additions & 2 deletions src/engine/contactwriter.cpp
Expand Up @@ -139,7 +139,7 @@ static const QString matchEmailAddressesTable(QString::fromLatin1("matchEmailAdd
static const QString matchPhoneNumbersTable(QString::fromLatin1("matchPhoneNumbers"));
static const QString matchOnlineAccountsTable(QString::fromLatin1("matchOnlineAccounts"));

ContactWriter::ContactWriter(const ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader)
ContactWriter::ContactWriter(ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader)
: m_engine(engine)
, m_database(database)
, m_notifier(notifier)
Expand Down Expand Up @@ -5625,7 +5625,7 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
query.bindValue(0, displayLabel);
const QString displayLabelGroup = m_database.determineDisplayLabelGroup(contact);
query.bindValue(1, displayLabelGroup);
const int displayLabelGroupSortOrder = m_database.possibleDisplayLabelGroups().indexOf(displayLabelGroup) + 1;
const int displayLabelGroupSortOrder = m_database.displayLabelGroupSortValue(displayLabelGroup);
query.bindValue(2, displayLabelGroupSortOrder);

query.bindValue(3, firstName);
Expand Down
4 changes: 2 additions & 2 deletions src/engine/contactwriter.h
Expand Up @@ -72,7 +72,7 @@ class ContactWriter
public:
typedef QList<QContactDetail::DetailType> DetailList;

ContactWriter(const ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader);
ContactWriter(ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader);
~ContactWriter();

QContactManager::Error save(
Expand Down Expand Up @@ -167,7 +167,7 @@ class ContactWriter

template <typename T> bool removeCommonDetails(quint32 contactId, QContactManager::Error *error);

const ContactsEngine &m_engine;
ContactsEngine &m_engine;
ContactsDatabase &m_database;
ContactNotifier *m_notifier;
ContactReader *m_reader;
Expand Down
7 changes: 7 additions & 0 deletions tests/auto/displaylabelgroups/test/test.pro
@@ -1,3 +1,10 @@
TARGET = tst_displaylabelgroups
include (../../../common.pri)

# We need access to QtContacts private headers
QT += contacts-private
# And we need access to the ContactManagerEngine header and moc output
INCLUDEPATH += ../../../../src/extensions/
HEADERS += ../../../../src/extensions/contactmanagerengine.h

SOURCES += tst_displaylabelgroups.cpp

0 comments on commit 787f62e

Please sign in to comment.