<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (<EMAIL>)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at <EMAIL>.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/




#include <QTimer>
#include <qtcontacts.h>
#include <QtTracker/ontologies/nie.h>
#include <QtTracker/ontologies/nco.h>
#include <qtrackercontactasyncrequest.h>
#include <QHash>

using namespace SopranoLive;

class ConversionLookup: public QHash<QString,QString>
{
public:
    ConversionLookup& operator<<(const QPair<QString, QString> &conversion)
    {
        this->insert(conversion.first, conversion.second);
        return *this;
    }
};


const QString FieldQContactLocalId("QContactLocalId");
const QString FieldAccountPath("AccountPath");
const ConversionLookup presenceConversion(ConversionLookup()
            <<QPair<QString, QString>("presence-status-offline", QContactOnlineAccount::PresenceOffline)
            <<QPair<QString, QString>("presence-status-available", QContactOnlineAccount::PresenceAvailable)
            <<QPair<QString, QString>("presence-status-away", QContactOnlineAccount::PresenceAway)
            <<QPair<QString, QString>("presence-status-extended-away", QContactOnlineAccount::PresenceExtendedAway)
            <<QPair<QString, QString>("presence-status-busy", QContactOnlineAccount::PresenceBusy)
            <<QPair<QString, QString>("presence-status-unknown", QContactOnlineAccount::PresenceUnknown)
            <<QPair<QString, QString>("presence-status-hidden", QContactOnlineAccount::PresenceHidden)
);

void matchPhoneNumber(RDFVariable &variable, QContactDetailFilter &filter)
{
    // This here is the first implementation of filtering that takes into account also affiliations.
    // needs to be applied for other filters too - TODO
    RDFVariable officeContact;
    RDFVariable homeContact;

    RDFVariable rdfPhoneNumber;
    rdfPhoneNumber = homeContact.property<nco::hasPhoneNumber>().property<nco::phoneNumber>();

    RDFVariable rdfOfficePhoneNumber;
    rdfOfficePhoneNumber = officeContact.property<nco::hasAffiliation>().property<nco::hasPhoneNumber>().property<nco::phoneNumber>();

    QString filterValue = filter.value().toString();
    if (filter.matchFlags() == Qt::MatchEndsWith)
    {
        QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Nokia","Trackerplugin");
        int matchDigitCount = settings.value("phoneNumberMatchDigitCount", "7").toInt();
        filterValue = filterValue.right(matchDigitCount);
        qDebug() << "match with:" << matchDigitCount << ":" << filterValue;
        rdfPhoneNumber.hasSuffix(filterValue);
        rdfOfficePhoneNumber.hasSuffix(filterValue);
    }
    else
    {   // default to exact match
        rdfOfficePhoneNumber.matchesRegexp(filterValue);
        rdfPhoneNumber.matchesRegexp(filterValue);
    }
    // This is the key part, including both contacts and affiliations
    variable.isMemberOf(RDFVariableList()<<homeContact);// TODO report bug doesnt work in tracker <<officeContact);
}

/*
 * RDFVariable describes all contacts in tracker before filter is applied.
 * This method translates QContactFilter to tracker rdf filter. When query is made
 * after this method, it would return only contacts that fit the filter.
 */
void QTrackerContactFetchRequest::applyFilterToContact(RDFVariable &variable,
        const QContactFilter &filter)
{
    if (filter.type() == QContactFilter::LocalIdFilter) {
        QContactLocalIdFilter filt = filter;
        if (!filt.ids().isEmpty()) {
            variable.property<nco::contactUID>().isMemberOf(filt.ids());
        } else {
            qWarning() << Q_FUNC_INFO << "QContactLocalIdFilter idlist is empty";
        }
    } else if (filter.type() == QContactFilter::ContactDetailFilter) {
        // this one is tricky as we need to match in contacts or in affiliations

        QContactDetailFilter filt = filter;
        if ( QContactPhoneNumber::DefinitionName == filt.detailDefinitionName()
             && QContactPhoneNumber::FieldNumber == filt.detailFieldName()) {
            matchPhoneNumber(variable, filt);
        }
        else if (filt.matchFlags() == Qt::MatchExactly) {
            if (QContactEmailAddress::DefinitionName == filt.detailDefinitionName()
                       && QContactEmailAddress::FieldEmailAddress == filt.detailFieldName()) {
                RDFVariable rdfEmailAddress;
                rdfEmailAddress = variable.property<nco::hasEmailAddress>();
                rdfEmailAddress.property<nco::emailAddress>() = LiteralValue(filt.value().toString());
            } else if (QContactOnlineAccount::DefinitionName == filt.detailDefinitionName()
                       && filt.detailFieldName() == "Account") {
                qDebug() << Q_FUNC_INFO;
                RDFVariable imaccount = variable.property<nco::hasIMAccount>();
                imaccount.property<nco::imID>().isMemberOf(QStringList()<<filt.value().toString());
            } else {
                qWarning() << "QContactTrackerEngine: Unsupported QContactFilter::ContactDetail"
                    << filt.detailDefinitionName();
            }
        }
    } else if (filter.type() == QContactFilter::ChangeLogFilter) {
        const QContactChangeLogFilter& clFilter = static_cast<const QContactChangeLogFilter&>(filter);
        // do not return facebook and telepathy contacts here
        // it is a temp implementation for the what to offer to synchronization constraint
        variable.property<nao::hasTag>().property<nao::prefLabel>() = LiteralValue("addressbook");

        if (clFilter.eventType() == QContactChangeLogFilter::EventRemoved) { // Removed since
            // did not find how to set errror to async request
            // error = QContactManager::NotSupportedError;
            qWarning() << "QContactTrackerEngine: Unsupported QContactChangeLogFilter::Removed (contacts removed since)";
        } else if (clFilter.eventType() == QContactChangeLogFilter::EventAdded) { // Added since
            variable.property<nie::contentCreated>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
        } else if (clFilter.eventType() == QContactChangeLogFilter::EventChanged) { // Changed since
            variable.property<nie::contentLastModified>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
        }
    }
}

/*
 * To understand why all the following methods have for affiliation param, check nco ontology:
 * every contact has all these properties and also linked to affiliations (also contacts - nco:Role)
 * that again have the same properties. So it was needed to make the same query 2-ce - once for contact
 * and once for affiliations
 */
RDFSelect preparePhoneNumbersQuery(RDFVariable &rdfcontact1, bool forAffiliations)
{
    RDFVariable phone;
    if (!forAffiliations)
        phone = rdfcontact1.property<nco::hasPhoneNumber>();
    else
        phone = rdfcontact1.property<nco::hasAffiliation>().property<nco::hasPhoneNumber>();
    RDFVariable type = phone.type();
    type.property<rdfs::subClassOf>().notEqual(nco::ContactMedium::iri()); // sparql cannot handle exact type but returns all super types as junk rows
    type.property<rdfs::subClassOf>().notEqual(rdfs::Resource::iri()); // sparql cannot handle exact type but returns all super types as junk rows
    // therefore we eliminate those rows that are not of interest
    // columns<commit_msg>New: Implemented QContactName and QContactUnionFilter with qtcontacts-tracker<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (<EMAIL>)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at <EMAIL>.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/




#include <QTimer>
#include <qtcontacts.h>
#include <QtTracker/ontologies/nie.h>
#include <QtTracker/ontologies/nco.h>
#include <qtrackercontactasyncrequest.h>
#include <QHash>

using namespace SopranoLive;

class ConversionLookup: public QHash<QString,QString>
{
public:
    ConversionLookup& operator<<(const QPair<QString, QString> &conversion)
    {
        this->insert(conversion.first, conversion.second);
        return *this;
    }
};


const QString FieldQContactLocalId("QContactLocalId");
const QString FieldAccountPath("AccountPath");
const ConversionLookup presenceConversion(ConversionLookup()
            <<QPair<QString, QString>("presence-status-offline", QContactOnlineAccount::PresenceOffline)
            <<QPair<QString, QString>("presence-status-available", QContactOnlineAccount::PresenceAvailable)
            <<QPair<QString, QString>("presence-status-away", QContactOnlineAccount::PresenceAway)
            <<QPair<QString, QString>("presence-status-extended-away", QContactOnlineAccount::PresenceExtendedAway)
            <<QPair<QString, QString>("presence-status-busy", QContactOnlineAccount::PresenceBusy)
            <<QPair<QString, QString>("presence-status-unknown", QContactOnlineAccount::PresenceUnknown)
            <<QPair<QString, QString>("presence-status-hidden", QContactOnlineAccount::PresenceHidden)
);

void matchPhoneNumber(RDFVariable &variable, QContactDetailFilter &filter)
{
    // This here is the first implementation of filtering that takes into account also affiliations.
    // needs to be applied for other filters too - TODO
    RDFVariable officeContact;
    RDFVariable homeContact;

    RDFVariable rdfPhoneNumber;
    rdfPhoneNumber = homeContact.property<nco::hasPhoneNumber>().property<nco::phoneNumber>();

    RDFVariable rdfOfficePhoneNumber;
    rdfOfficePhoneNumber = officeContact.property<nco::hasAffiliation>().property<nco::hasPhoneNumber>().property<nco::phoneNumber>();

    QString filterValue = filter.value().toString();
    if (filter.matchFlags() == Qt::MatchEndsWith)
    {
        QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Nokia","Trackerplugin");
        int matchDigitCount = settings.value("phoneNumberMatchDigitCount", "7").toInt();
        filterValue = filterValue.right(matchDigitCount);
        qDebug() << "match with:" << matchDigitCount << ":" << filterValue;
        rdfPhoneNumber.hasSuffix(filterValue);
        rdfOfficePhoneNumber.hasSuffix(filterValue);
    }
    else
    {   // default to exact match
        rdfOfficePhoneNumber.matchesRegexp(filterValue);
        rdfPhoneNumber.matchesRegexp(filterValue);
    }
    // This is the key part, including both contacts and affiliations
    variable.isMemberOf(RDFVariableList()<<homeContact);// TODO report bug doesnt work in tracker <<officeContact);
}

void matchName(RDFVariable &variable, QContactDetailFilter &filter)
{
    if (filter.detailDefinitionName() != QContactName::DefinitionName) {
        qWarning() << "QTrackerContactFetchRequest," << __FUNCTION__
                   << "Unsupported definition name in detail filter, should be QContactName::DefinitionName";
        return;
    }
    QString filterValue = filter.value().toString();
    QString field = filter.detailFieldName();
    if ((filter.matchFlags() & QContactFilter::MatchExactly) == QContactFilter::MatchExactly) {
        if (field == QContactName::FieldFirst) {
            variable.property<nco::nameGiven>() = LiteralValue(filterValue);
        } else if (field == QContactName::FieldLast) {
            variable.property<nco::nameFamily>() = LiteralValue(filterValue);
        } else if (field == QContactName::FieldMiddle) {
            variable.property<nco::nameAdditional>() = LiteralValue(filterValue);
        } else if (field == QContactName::FieldPrefix) {
            variable.property<nco::nameHonorificPrefix>() = LiteralValue(filterValue);
        } else if (field == QContactName::FieldSuffix) {
            variable.property<nco::nameHonorificSuffix>() = LiteralValue(filterValue);
        }
    } else {
        qWarning() << "QTrackerContactFetchRequest," << __FUNCTION__
                   << "Unsupported match flag in detail filter by QContactName";
    }
}

/*
 * RDFVariable describes all contacts in tracker before filter is applied.
 * This method translates QContactFilter to tracker rdf filter. When query is made
 * after this method, it would return only contacts that fit the filter.
 */
void QTrackerContactFetchRequest::applyFilterToContact(RDFVariable &variable,
        const QContactFilter &filter)
{
    if (filter.type() == QContactFilter::LocalIdFilter) {
        QContactLocalIdFilter filt = filter;
        if (!filt.ids().isEmpty()) {
            variable.property<nco::contactUID>().isMemberOf(filt.ids());
        } else {
            qWarning() << Q_FUNC_INFO << "QContactLocalIdFilter idlist is empty";
        }
    } else if (filter.type() == QContactFilter::ContactDetailFilter) {
        // this one is tricky as we need to match in contacts or in affiliations

        QContactDetailFilter filt = filter;
        if ( QContactPhoneNumber::DefinitionName == filt.detailDefinitionName()
             && QContactPhoneNumber::FieldNumber == filt.detailFieldName()) {
            matchPhoneNumber(variable, filt);
        }
        else if (QContactName::DefinitionName == filt.detailDefinitionName()) {
            matchName(variable, filt);
        }
        else if (filt.matchFlags() == Qt::MatchExactly) {
            if (QContactEmailAddress::DefinitionName == filt.detailDefinitionName()
                       && QContactEmailAddress::FieldEmailAddress == filt.detailFieldName()) {
                RDFVariable rdfEmailAddress;
                rdfEmailAddress = variable.property<nco::hasEmailAddress>();
                rdfEmailAddress.property<nco::emailAddress>() = LiteralValue(filt.value().toString());
            } else if (QContactOnlineAccount::DefinitionName == filt.detailDefinitionName()
                       && filt.detailFieldName() == "Account") {
                qDebug() << Q_FUNC_INFO;
                RDFVariable imaccount = variable.property<nco::hasIMAccount>();
                imaccount.property<nco::imID>().isMemberOf(QStringList()<<filt.value().toString());
            } else {
                qWarning() << "QContactTrackerEngine: Unsupported QContactFilter::ContactDetail"
                    << filt.detailDefinitionName();
            }
        }
    } else if (filter.type() == QContactFilter::ChangeLogFilter) {
        const QContactChangeLogFilter& clFilter = static_cast<const QContactChangeLogFilter&>(filter);
        // do not return facebook and telepathy contacts here
        // it is a temp implementation for the what to offer to synchronization constraint
        variable.property<nao::hasTag>().property<nao::prefLabel>() = LiteralValue("addressbook");

        if (clFilter.eventType() == QContactChangeLogFilter::EventRemoved) { // Removed since
            // did not find how to set errror to async request
            // error = QContactManager::NotSupportedError;
            qWarning() << "QContactTrackerEngine: Unsupported QContactChangeLogFilter::Removed (contacts removed since)";
        } else if (clFilter.eventType() == QContactChangeLogFilter::EventAdded) { // Added since
            variable.property<nie::contentCreated>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
        } else if (clFilter.eventType() == QContactChangeLogFilter::EventChanged) { // Changed since
            variable.property<nie::contentLastModified>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
        }
    } else if (filter.type() == QContactFilter::UnionFilter) {
        const QContactUnionFilter unionFilter(filter);
        foreach (QContactFilter f, unionFilter.filters()) {
            applyFilterToContact(variable, f);
        }
    }
}

/*
 * To understand why all the following methods have for affiliation param, check nco ontology:
 * every contact has all these properties and also linked to affiliations (also contacts - nco:Role)
 * that again have the same properties. So it was needed to make the same query 2-ce - once for contact
 * and once for affiliations
 */
RDFSelect preparePhoneNumbersQuery(RDFVariable &rdfcontact1, bool forAffiliations)
{
    RDFVariable phone;
    if (!forAffiliations)
        phone = rdfcontact1.property<nco::hasPhoneNumber>();
    else
        phone = rdfcontact1.property<nco::hasAffiliation>().property<nco::hasPhoneNumber>();
    RDFVariable type = phone.type();
    type.property<rdfs::subClassOf>().notEqual(nco::ContactMedium::iri()); // sparql cannot handle exact type but returns all super types as junk rows
    type.property<rdfs::subClassOf>().notEqual(rdfs::Resource::iri()); // sparql cannot handle exact type but returns all super types as junk rows
    // therefore we eliminate those rows that are not of interest
    // columns
{'fraction_non_alphanumeric': 0.12520156046814043, 'fraction_numerical': 0.0017685305591677502, 'mean_word_length': 3.87227572225038, 'pattern_counts': {'":': 2, '<': 178, '<?xml version=': 0, '>': 88, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '498352', 'n_tokens_mistral': 5168, 'n_tokens_neox': 4986, 'n_words': 1334}