<commit_before>//
//     Copyright 2011, Kay Hayen, mailto:kayhayen@gmx.de
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     If you submit Kay Hayen patches to this software in either form, you
//     automatically grant him a copyright assignment to the code, or in the
//     alternative a BSD license to the code, should your jurisdiction prevent
//     this. Obviously it won't affect code that comes to him indirectly or
//     code you don't submit to him.
//
//     This is to reserve my ability to re-license the code at any time, e.g.
//     the PSF. With this version of Nuitka, using it for Closed Source will
//     not be allowed.
//
//     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, version 3 of the License.
//
//     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, see <http://www.gnu.org/licenses/>.
//
//     Please leave the whole of this copyright notice intact.
//
#ifndef __NUITKA_VARIABLES_TEMPORARY_H__
#define __NUITKA_VARIABLES_TEMPORARY_H__

// Wraps a PyObject * you received or acquired from another container to simplify refcount
// handling when you're not going to use the object beyond the local scope. It will hold a
// reference to the wrapped object as long as the PyObjectTemporary is alive, and will
// release the reference when the wrapper is destroyed: this eliminates the need for
// manual DECREF calls on Python objects before returning from a method call.
//
// In effect, wrapping an object inside a PyObjectTemporary is equivalent to a deferred
// Py_DECREF() call on the wrapped object.

class PyObjectTemporary
{
    public:
        explicit PyObjectTemporary( PyObject *object )
        {
            assertObject( object );

            this->object = object;
        }

        ~PyObjectTemporary()
        {
            assertObject( this->object );

            Py_DECREF( this->object );
        }

        PyObject *asObject() const
        {
            assertObject( this->object );

            return this->object;
        }

        void assign( PyObject *object )
        {
            assertObject( this->object );

            Py_DECREF( this->object );

            assertObject( object );

            this->object = object;
        }

    private:
        PyObjectTemporary( const PyObjectTemporary &object ) = delete;

        PyObject *object;
};

class PyObjectTempHolder
{
    public:
        explicit PyObjectTempHolder( PyObject *object )
        {
            assertObject( object );

            this->object = object;
        }

        ~PyObjectTempHolder()
        {
            Py_XDECREF( this->object );
        }

        PyObject *asObject()
        {
            assertObject( this->object );

            PyObject *result = this->object;
            this->object = NULL;
            return result;
        }

    private:
        PyObjectTempHolder( const PyObjectTempHolder &object ) = delete;

        PyObject *object;
};

#endif
<commit_msg>Minor comment improvement<commit_after>//
//     Copyright 2011, Kay Hayen, mailto:kayhayen@gmx.de
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     If you submit Kay Hayen patches to this software in either form, you
//     automatically grant him a copyright assignment to the code, or in the
//     alternative a BSD license to the code, should your jurisdiction prevent
//     this. Obviously it won't affect code that comes to him indirectly or
//     code you don't submit to him.
//
//     This is to reserve my ability to re-license the code at any time, e.g.
//     the PSF. With this version of Nuitka, using it for Closed Source will
//     not be allowed.
//
//     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, version 3 of the License.
//
//     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, see <http://www.gnu.org/licenses/>.
//
//     Please leave the whole of this copyright notice intact.
//
#ifndef __NUITKA_VARIABLES_TEMPORARY_H__
#define __NUITKA_VARIABLES_TEMPORARY_H__

// Wraps a "PyObject *" you received or acquired from another container to simplify refcount
// handling when you're not going to use the object beyond the local scope. It will hold a
// reference to the wrapped object as long as the PyObjectTemporary is alive, and will
// release the reference when the wrapper is destroyed: this eliminates the need for
// manual DECREF calls on Python objects before returning from a method call.
//
// In effect, wrapping an object inside a PyObjectTemporary is equivalent to a deferred
// Py_DECREF() call on the wrapped object.

class PyObjectTemporary
{
    public:
        explicit PyObjectTemporary( PyObject *object )
        {
            assertObject( object );

            this->object = object;
        }

        ~PyObjectTemporary()
        {
            assertObject( this->object );

            Py_DECREF( this->object );
        }

        PyObject *asObject() const
        {
            assertObject( this->object );

            return this->object;
        }

        void assign( PyObject *object )
        {
            assertObject( this->object );

            Py_DECREF( this->object );

            assertObject( object );

            this->object = object;
        }

    private:
        PyObjectTemporary( const PyObjectTemporary &object ) = delete;

        PyObject *object;
};

class PyObjectTempHolder
{
    public:
        explicit PyObjectTempHolder( PyObject *object )
        {
            assertObject( object );

            this->object = object;
        }

        ~PyObjectTempHolder()
        {
            Py_XDECREF( this->object );
        }

        PyObject *asObject()
        {
            assertObject( this->object );

            PyObject *result = this->object;
            this->object = NULL;
            return result;
        }

    private:
        PyObjectTempHolder( const PyObjectTempHolder &object ) = delete;

        PyObject *object;
};

#endif
<|endoftext|>


<commit_before>/** This file is part of Contacts daemon
 **
 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
 **
 ** Contact:  Nokia Corporation (info@qt.nokia.com)
 **
 ** GNU Lesser General Public License Usage
 ** 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.
 **
 ** Other Usage
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **/

#include "cdbirthdaycontroller.h"
#include "cdbirthdaycalendar.h"
#include "cdbirthdayplugin.h"
#include "debug.h"

#include <QDir>
#include <QFile>

#include <cubi.h>
#include <ontologies.h>

#include <QContactBirthday>
#include <QContactDetailFilter>
#include <QContactDisplayLabel>
#include <QContactFetchRequest>
#include <QContactLocalIdFilter>

QTM_USE_NAMESPACE

CUBI_USE_NAMESPACE
CUBI_USE_NAMESPACE_RESOURCES

using namespace Contactsd;

// The logic behind this class was strongly influenced by QctTrackerChangeListener in
// qtcontacts-tracker.
CDBirthdayController::CDBirthdayController(QSparqlConnection &connection,
                                           QObject *parent)
    : QObject(parent)
    , mSparqlConnection(connection)
    , mCalendar(0)
    , mManager(0)
{
    const QLatin1String trackerManagerName = QLatin1String("tracker");

    mManager = new QContactManager(trackerManagerName, QMap<QString, QString>(), this);

    if (mManager->managerName() != trackerManagerName) {
        debug() << Q_FUNC_INFO << "Tracker plugin not found";
        return;
    }

    fetchTrackerIds();

    if (not stampFileExists()) {
        // Delete the calendar database.
        mCalendar = new CDBirthdayCalendar(CDBirthdayCalendar::FullSync, this);

        updateAllBirthdays();
    } else {
        // Use the existing calendar database.
        mCalendar = new CDBirthdayCalendar(CDBirthdayCalendar::Incremental, this);
    }
}

CDBirthdayController::~CDBirthdayController()
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Tracker ID fetching
///////////////////////////////////////////////////////////////////////////////////////////////////

void
CDBirthdayController::fetchTrackerIds()
{
    // keep in sync with the enum in the header and NTrackerIds
    const QList<ResourceValue> resources = QList<ResourceValue>()
                                           << nco::birthDate::resource()
                                           << rdf::type::resource()
                                           << nco::PersonContact::resource()
                                           << nco::ContactGroup::resource();

    Select select;

    foreach (const ResourceValue &value, resources) {
        select.addProjection(Functions::trackerId.apply(value));
    }

    if (not mSparqlConnection.isValid()) {
        debug() << Q_FUNC_INFO << "SPARQL connection is not valid";
        return;
    }

    QScopedPointer<QSparqlResult> result(mSparqlConnection.exec(QSparqlQuery(select.sparql())));

    if (result->hasError()) {
        debug() << Q_FUNC_INFO << "Could not fetch Tracker IDs:" << result->lastError().message();
        return;
    }

    connect(result.take(), SIGNAL(finished()), this, SLOT(onTrackerIdsFetched()));
}

void
CDBirthdayController::onTrackerIdsFetched()
{
    QSparqlResult *result = qobject_cast<QSparqlResult*>(sender());

    if (result == 0) {
        debug() << Q_FUNC_INFO << "Invalid result";
        return;
    }

    if (result->hasError()) {
        debug() << Q_FUNC_INFO << "Could not fetch Tracker IDs:" << result->lastError().message();
    } else if (not result->next()) {
        debug() << Q_FUNC_INFO << "No results returned";
        return;
    } else {
        const QSparqlResultRow row = result->current();

        for (int i = 0; i < NTrackerIds; ++i) {
            mTrackerIds[i] = row.value(i).toInt();
        }
    }

    // Provide hint we are done with this result.
    result->deleteLater();

    debug() << Q_FUNC_INFO << "Tracker IDs fetched, connecting the change notifier";

    connectChangeNotifier();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Contact monitor
///////////////////////////////////////////////////////////////////////////////////////////////////

void
CDBirthdayController::connectChangeNotifier()
{
    const QStringList contactClassIris = QStringList()
                                      << nco::PersonContact::iri()
                                      << nco::ContactGroup::iri();

    foreach (const QString &iri, contactClassIris) {
        connect(new TrackerChangeNotifier(iri, this),
            SIGNAL(changed(QList<TrackerChangeNotifier::Quad>,
                           QList<TrackerChangeNotifier::Quad>)),
            SLOT(onGraphChanged(QList<TrackerChangeNotifier::Quad>,
                                QList<TrackerChangeNotifier::Quad>)));
    }
}

void
CDBirthdayController::onGraphChanged(const QList<TrackerChangeNotifier::Quad>& deletions,
                                     const QList<TrackerChangeNotifier::Quad>& insertions)
{
    mDeleteNotifications += deletions;
    mInsertNotifications += insertions;

    if (isDebugEnabled()) {
        TrackerChangeNotifier const * const notifier = qobject_cast<TrackerChangeNotifier *>(sender());

        if (notifier == 0) {
            warning() << Q_FUNC_INFO << "Error casting birthday change notifier";
            return;
        }

        debug() << notifier->watchedClass() << "birthday: deletions:" << deletions;
        debug() << notifier->watchedClass() << "birthday: insertions:" << insertions;
    }

    processNotificationQueues();
}

void
CDBirthdayController::processNotifications(QList<TrackerChangeNotifier::Quad> &notifications,
                                           QSet<QContactLocalId> &propertyChanges,
                                           QSet<QContactLocalId> &resourceChanges)
{
    foreach (const TrackerChangeNotifier::Quad &quad, notifications) {
        if (quad.predicate == mTrackerIds[NcoBirthDate]) {
            propertyChanges += quad.subject;
            continue;
        }

        if (quad.predicate == mTrackerIds[RdfType]) {
            if (quad.object == mTrackerIds[NcoPersonContact]
             || quad.object == mTrackerIds[NcoContactGroup]) {
                resourceChanges += quad.subject;
            }
        }
    }

    // Remove the processed notifications.
    notifications.clear();
}

void
CDBirthdayController::processNotificationQueues()
{
    QSet<QContactLocalId> insertedContacts;
    QSet<QContactLocalId> deletedContacts;
    QSet<QContactLocalId> birthdayChangedIds;

    // Process notification queues to determine contacts with changed birthdays.
    processNotifications(mDeleteNotifications, birthdayChangedIds, deletedContacts);
    processNotifications(mInsertNotifications, birthdayChangedIds, insertedContacts);

    if (isDebugEnabled()) {
        debug() << "changed birthdates: " << birthdayChangedIds.count() << birthdayChangedIds;
    }

    // Remove the birthdays for contacts that are not there anymore
    foreach (QContactLocalId id, deletedContacts) {
        mCalendar->deleteBirthday(id);
    }

    // Update the calendar with the birthday changes.
    if (not birthdayChangedIds.isEmpty()) {
        fetchContacts(birthdayChangedIds.toList());
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Full sync logic
///////////////////////////////////////////////////////////////////////////////////////////////////

bool
CDBirthdayController::stampFileExists()
{
    const QFile cacheFile(stampFilePath(), this);

    return cacheFile.exists();
}

void
CDBirthdayController::createStampFile()
{
    QFile cacheFile(stampFilePath(), this);

    if (not cacheFile.exists()) {
        if (not cacheFile.open(QIODevice::WriteOnly)) {
            warning() << Q_FUNC_INFO << "Unable to create birthday plugin stamp file "
                                     << cacheFile.fileName() << " with error " << cacheFile.errorString();
        } else {
            cacheFile.close();
        }
    }
}

QString
CDBirthdayController::stampFilePath() const
{
    return BasePlugin::cacheFileName(QLatin1String("calendar.stamp"));
}

void
CDBirthdayController::updateAllBirthdays()
{
    QContactFetchHint fetchHint;
    static const QStringList detailDefinitions = QStringList() << QContactBirthday::DefinitionName
                                                               << QContactDisplayLabel::DefinitionName;
    fetchHint.setDetailDefinitionsHint(detailDefinitions);

    // Filter on any contact with a birthday.
    QContactDetailFilter fetchFilter;
    fetchFilter.setDetailDefinitionName(QContactBirthday::DefinitionName);

    QContactFetchRequest * const fetchRequest = new QContactFetchRequest(this);
    fetchRequest->setManager(mManager);
    fetchRequest->setFetchHint(fetchHint);
    fetchRequest->setFilter(fetchFilter);

    connect(fetchRequest,
            SIGNAL(stateChanged(QContactAbstractRequest::State)),
            SLOT(onFullSyncRequestStateChanged(QContactAbstractRequest::State)));

    if (not fetchRequest->start()) {
        warning() << Q_FUNC_INFO << "Unable to start contact birthdays fetch request";
        delete fetchRequest;
        return;
    }

    debug() << "Contact birthdays fetch request started";
}

void
CDBirthdayController::onFullSyncRequestStateChanged(QContactAbstractRequest::State newState)
{
    QContactFetchRequest * const fetchRequest = qobject_cast<QContactFetchRequest*>(sender());

    if (not processFetchRequest(fetchRequest, newState)) {
        return;
    }

    updateBirthdays(fetchRequest->contacts());

    // Create the stamp file only after a successful full sync.
    createStampFile();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Incremental sync logic
///////////////////////////////////////////////////////////////////////////////////////////////////

void
CDBirthdayController::fetchContacts(const QList<QContactLocalId> &contactIds)
{
    QContactFetchHint fetchHint;
    static const QStringList detailDefinitions = QStringList() << QContactBirthday::DefinitionName
                                                               << QContactDisplayLabel::DefinitionName;
    fetchHint.setDetailDefinitionsHint(detailDefinitions);

    QContactLocalIdFilter fetchFilter;
    fetchFilter.setIds(contactIds);

    QContactFetchRequest * const fetchRequest = new QContactFetchRequest(this);
    fetchRequest->setManager(mManager);
    fetchRequest->setFetchHint(fetchHint);
    fetchRequest->setFilter(fetchFilter);

    connect(fetchRequest,
            SIGNAL(stateChanged(QContactAbstractRequest::State)),
            SLOT(onFetchRequestStateChanged(QContactAbstractRequest::State)));

    if (not fetchRequest->start()) {
        warning() << Q_FUNC_INFO << "Unable to start birthday contact fetch request";
        delete fetchRequest;
        return;
    }

    debug() << "Birthday contacts fetch request started";
}

void
CDBirthdayController::onFetchRequestStateChanged(QContactAbstractRequest::State newState)
{
    QContactFetchRequest * const fetchRequest = qobject_cast<QContactFetchRequest*>(sender());

    if (not processFetchRequest(fetchRequest, newState)) {
        return;
    }

    updateBirthdays(fetchRequest->contacts());
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Common sync logic
///////////////////////////////////////////////////////////////////////////////////////////////////

bool
CDBirthdayController::processFetchRequest(QContactFetchRequest * const fetchRequest,
                                          QContactAbstractRequest::State newState)
{
    if (fetchRequest == 0) {
        warning() << Q_FUNC_INFO << "Invalid fetch request";
        return false;
    }

    bool success = false;

    switch (newState) {
    case QContactAbstractRequest::FinishedState:
        debug() << "Birthday contacts fetch request finished";

        if (fetchRequest->error() != QContactManager::NoError) {
            warning() << Q_FUNC_INFO << "Error during birthday contact fetch request, code: "
                      << fetchRequest->error();
        } else {
            success = true;
        }

        break;

    case QContactAbstractRequest::CanceledState:
        break;

    default:
        return false;
    }

    // Provide hint we are done with this request.
    fetchRequest->deleteLater();

    return success;
}

void
CDBirthdayController::updateBirthdays(const QList<QContact> &changedBirthdays)
{
    foreach (const QContact &contact, changedBirthdays) {
        const QContactBirthday contactBirthday = contact.detail<QContactBirthday>();
        const QContactDisplayLabel contactDisplayLabel = contact.detail<QContactDisplayLabel>();
        const QDate calendarBirthday = mCalendar->birthdayDate(contact.localId());
        const QString calendarSummary = mCalendar->summary(contact.localId());

        // Display label or birthdate was removed from the contact, so delete it from the calendar.
        if (contactDisplayLabel.label().isNull() || contactBirthday.date().isNull()) {
            if (isDebugEnabled()) {
                debug() << "Contact: " << contact << " removed birthday or displayLabel, so delete the calendar event";
            }

            mCalendar->deleteBirthday(contact.localId());
        // Display label or birthdate was changed on the contact, so update the calendar.
        } else if ((contactDisplayLabel.label() != calendarSummary) ||
                   (contactBirthday.date() != calendarBirthday)) {
            if (isDebugEnabled()) {
                debug() << "Contact with calendar birthday: " << contactBirthday.date()
                        << " and calendar displayLabel: " << calendarSummary
                        << " changed details to: " << contact << ", so update the calendar event";
            }

            mCalendar->updateBirthday(contact);
        }
    }
}
<commit_msg>Changes: Merge common logic in contact fetching callbacks<commit_after>/** This file is part of Contacts daemon
 **
 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
 **
 ** Contact:  Nokia Corporation (info@qt.nokia.com)
 **
 ** GNU Lesser General Public License Usage
 ** 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.
 **
 ** Other Usage
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **/

#include "cdbirthdaycontroller.h"
#include "cdbirthdaycalendar.h"
#include "cdbirthdayplugin.h"
#include "debug.h"

#include <QDir>
#include <QFile>

#include <cubi.h>
#include <ontologies.h>

#include <QContactBirthday>
#include <QContactDetailFilter>
#include <QContactDisplayLabel>
#include <QContactFetchRequest>
#include <QContactLocalIdFilter>

QTM_USE_NAMESPACE

CUBI_USE_NAMESPACE
CUBI_USE_NAMESPACE_RESOURCES

using namespace Contactsd;

// The logic behind this class was strongly influenced by QctTrackerChangeListener in
// qtcontacts-tracker.
CDBirthdayController::CDBirthdayController(QSparqlConnection &connection,
                                           QObject *parent)
    : QObject(parent)
    , mSparqlConnection(connection)
    , mCalendar(0)
    , mManager(0)
{
    const QLatin1String trackerManagerName = QLatin1String("tracker");

    mManager = new QContactManager(trackerManagerName, QMap<QString, QString>(), this);

    if (mManager->managerName() != trackerManagerName) {
        debug() << Q_FUNC_INFO << "Tracker plugin not found";
        return;
    }

    fetchTrackerIds();

    if (not stampFileExists()) {
        // Delete the calendar database.
        mCalendar = new CDBirthdayCalendar(CDBirthdayCalendar::FullSync, this);

        updateAllBirthdays();
    } else {
        // Use the existing calendar database.
        mCalendar = new CDBirthdayCalendar(CDBirthdayCalendar::Incremental, this);
    }
}

CDBirthdayController::~CDBirthdayController()
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Tracker ID fetching
///////////////////////////////////////////////////////////////////////////////////////////////////

void
CDBirthdayController::fetchTrackerIds()
{
    // keep in sync with the enum in the header and NTrackerIds
    const QList<ResourceValue> resources = QList<ResourceValue>()
                                           << nco::birthDate::resource()
                                           << rdf::type::resource()
                                           << nco::PersonContact::resource()
                                           << nco::ContactGroup::resource();

    Select select;

    foreach (const ResourceValue &value, resources) {
        select.addProjection(Functions::trackerId.apply(value));
    }

    if (not mSparqlConnection.isValid()) {
        debug() << Q_FUNC_INFO << "SPARQL connection is not valid";
        return;
    }

    QScopedPointer<QSparqlResult> result(mSparqlConnection.exec(QSparqlQuery(select.sparql())));

    if (result->hasError()) {
        debug() << Q_FUNC_INFO << "Could not fetch Tracker IDs:" << result->lastError().message();
        return;
    }

    connect(result.take(), SIGNAL(finished()), this, SLOT(onTrackerIdsFetched()));
}

void
CDBirthdayController::onTrackerIdsFetched()
{
    QSparqlResult *result = qobject_cast<QSparqlResult*>(sender());

    if (result == 0) {
        debug() << Q_FUNC_INFO << "Invalid result";
        return;
    }

    if (result->hasError()) {
        debug() << Q_FUNC_INFO << "Could not fetch Tracker IDs:" << result->lastError().message();
    } else if (not result->next()) {
        debug() << Q_FUNC_INFO << "No results returned";
        return;
    } else {
        const QSparqlResultRow row = result->current();

        for (int i = 0; i < NTrackerIds; ++i) {
            mTrackerIds[i] = row.value(i).toInt();
        }
    }

    // Provide hint we are done with this result.
    result->deleteLater();

    debug() << Q_FUNC_INFO << "Tracker IDs fetched, connecting the change notifier";

    connectChangeNotifier();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Contact monitor
///////////////////////////////////////////////////////////////////////////////////////////////////

void
CDBirthdayController::connectChangeNotifier()
{
    const QStringList contactClassIris = QStringList()
                                      << nco::PersonContact::iri()
                                      << nco::ContactGroup::iri();

    foreach (const QString &iri, contactClassIris) {
        connect(new TrackerChangeNotifier(iri, this),
            SIGNAL(changed(QList<TrackerChangeNotifier::Quad>,
                           QList<TrackerChangeNotifier::Quad>)),
            SLOT(onGraphChanged(QList<TrackerChangeNotifier::Quad>,
                                QList<TrackerChangeNotifier::Quad>)));
    }
}

void
CDBirthdayController::onGraphChanged(const QList<TrackerChangeNotifier::Quad>& deletions,
                                     const QList<TrackerChangeNotifier::Quad>& insertions)
{
    mDeleteNotifications += deletions;
    mInsertNotifications += insertions;

    if (isDebugEnabled()) {
        TrackerChangeNotifier const * const notifier = qobject_cast<TrackerChangeNotifier *>(sender());

        if (notifier == 0) {
            warning() << Q_FUNC_INFO << "Error casting birthday change notifier";
            return;
        }

        debug() << notifier->watchedClass() << "birthday: deletions:" << deletions;
        debug() << notifier->watchedClass() << "birthday: insertions:" << insertions;
    }

    processNotificationQueues();
}

void
CDBirthdayController::processNotifications(QList<TrackerChangeNotifier::Quad> &notifications,
                                           QSet<QContactLocalId> &propertyChanges,
                                           QSet<QContactLocalId> &resourceChanges)
{
    foreach (const TrackerChangeNotifier::Quad &quad, notifications) {
        if (quad.predicate == mTrackerIds[NcoBirthDate]) {
            propertyChanges += quad.subject;
            continue;
        }

        if (quad.predicate == mTrackerIds[RdfType]) {
            if (quad.object == mTrackerIds[NcoPersonContact]
             || quad.object == mTrackerIds[NcoContactGroup]) {
                resourceChanges += quad.subject;
            }
        }
    }

    // Remove the processed notifications.
    notifications.clear();
}

void
CDBirthdayController::processNotificationQueues()
{
    QSet<QContactLocalId> insertedContacts;
    QSet<QContactLocalId> deletedContacts;
    QSet<QContactLocalId> birthdayChangedIds;

    // Process notification queues to determine contacts with changed birthdays.
    processNotifications(mDeleteNotifications, birthdayChangedIds, deletedContacts);
    processNotifications(mInsertNotifications, birthdayChangedIds, insertedContacts);

    if (isDebugEnabled()) {
        debug() << "changed birthdates: " << birthdayChangedIds.count() << birthdayChangedIds;
    }

    // Remove the birthdays for contacts that are not there anymore
    foreach (QContactLocalId id, deletedContacts) {
        mCalendar->deleteBirthday(id);
    }

    // Update the calendar with the birthday changes.
    if (not birthdayChangedIds.isEmpty()) {
        fetchContacts(birthdayChangedIds.toList());
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Full sync logic
///////////////////////////////////////////////////////////////////////////////////////////////////

bool
CDBirthdayController::stampFileExists()
{
    const QFile cacheFile(stampFilePath(), this);

    return cacheFile.exists();
}

void
CDBirthdayController::createStampFile()
{
    QFile cacheFile(stampFilePath(), this);

    if (not cacheFile.exists()) {
        if (not cacheFile.open(QIODevice::WriteOnly)) {
            warning() << Q_FUNC_INFO << "Unable to create birthday plugin stamp file "
                                     << cacheFile.fileName() << " with error " << cacheFile.errorString();
        } else {
            cacheFile.close();
        }
    }
}

QString
CDBirthdayController::stampFilePath() const
{
    return BasePlugin::cacheFileName(QLatin1String("calendar.stamp"));
}

void
CDBirthdayController::updateAllBirthdays()
{
    QContactFetchHint fetchHint;
    static const QStringList detailDefinitions = QStringList() << QContactBirthday::DefinitionName
                                                               << QContactDisplayLabel::DefinitionName;
    fetchHint.setDetailDefinitionsHint(detailDefinitions);

    // Filter on any contact with a birthday.
    QContactDetailFilter fetchFilter;
    fetchFilter.setDetailDefinitionName(QContactBirthday::DefinitionName);

    QContactFetchRequest * const fetchRequest = new QContactFetchRequest(this);
    fetchRequest->setManager(mManager);
    fetchRequest->setFetchHint(fetchHint);
    fetchRequest->setFilter(fetchFilter);

    connect(fetchRequest,
            SIGNAL(stateChanged(QContactAbstractRequest::State)),
            SLOT(onFullSyncRequestStateChanged(QContactAbstractRequest::State)));

    if (not fetchRequest->start()) {
        warning() << Q_FUNC_INFO << "Unable to start contact birthdays fetch request";
        delete fetchRequest;
        return;
    }

    debug() << "Contact birthdays fetch request started";
}

void
CDBirthdayController::onFullSyncRequestStateChanged(QContactAbstractRequest::State newState)
{
    if (processFetchRequest(qobject_cast<QContactFetchRequest*>(sender()), newState)) {
        // Create the stamp file only after a successful full sync.
        createStampFile();
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Incremental sync logic
///////////////////////////////////////////////////////////////////////////////////////////////////

void
CDBirthdayController::fetchContacts(const QList<QContactLocalId> &contactIds)
{
    QContactFetchHint fetchHint;
    static const QStringList detailDefinitions = QStringList() << QContactBirthday::DefinitionName
                                                               << QContactDisplayLabel::DefinitionName;
    fetchHint.setDetailDefinitionsHint(detailDefinitions);

    QContactLocalIdFilter fetchFilter;
    fetchFilter.setIds(contactIds);

    QContactFetchRequest * const fetchRequest = new QContactFetchRequest(this);
    fetchRequest->setManager(mManager);
    fetchRequest->setFetchHint(fetchHint);
    fetchRequest->setFilter(fetchFilter);

    connect(fetchRequest,
            SIGNAL(stateChanged(QContactAbstractRequest::State)),
            SLOT(onFetchRequestStateChanged(QContactAbstractRequest::State)));

    if (not fetchRequest->start()) {
        warning() << Q_FUNC_INFO << "Unable to start birthday contact fetch request";
        delete fetchRequest;
        return;
    }

    debug() << "Birthday contacts fetch request started";
}

void
CDBirthdayController::onFetchRequestStateChanged(QContactAbstractRequest::State newState)
{
    processFetchRequest(qobject_cast<QContactFetchRequest*>(sender()), newState);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Common sync logic
///////////////////////////////////////////////////////////////////////////////////////////////////

bool
CDBirthdayController::processFetchRequest(QContactFetchRequest *const fetchRequest,
                                          QContactAbstractRequest::State newState)
{
    if (fetchRequest == 0) {
        warning() << Q_FUNC_INFO << "Invalid fetch request";
        return false;
    }

    bool success = false;

    switch (newState) {
    case QContactAbstractRequest::FinishedState:
        debug() << "Birthday contacts fetch request finished";

        if (fetchRequest->error() != QContactManager::NoError) {
            warning() << Q_FUNC_INFO << "Error during birthday contact fetch request, code: "
                      << fetchRequest->error();
        } else {
            updateBirthdays(fetchRequest->contacts());
            success = true;
        }

        break;

    case QContactAbstractRequest::CanceledState:
        break;

    default:
        return false;
    }

    // Provide hint we are done with this request.
    fetchRequest->deleteLater();

    return success;
}

void
CDBirthdayController::updateBirthdays(const QList<QContact> &changedBirthdays)
{
    foreach (const QContact &contact, changedBirthdays) {
        const QContactBirthday contactBirthday = contact.detail<QContactBirthday>();
        const QContactDisplayLabel contactDisplayLabel = contact.detail<QContactDisplayLabel>();
        const QDate calendarBirthday = mCalendar->birthdayDate(contact.localId());
        const QString calendarSummary = mCalendar->summary(contact.localId());

        // Display label or birthdate was removed from the contact, so delete it from the calendar.
        if (contactDisplayLabel.label().isNull() || contactBirthday.date().isNull()) {
            if (isDebugEnabled()) {
                debug() << "Contact: " << contact << " removed birthday or displayLabel, so delete the calendar event";
            }

            mCalendar->deleteBirthday(contact.localId());
        // Display label or birthdate was changed on the contact, so update the calendar.
        } else if ((contactDisplayLabel.label() != calendarSummary) ||
                   (contactBirthday.date() != calendarBirthday)) {
            if (isDebugEnabled()) {
                debug() << "Contact with calendar birthday: " << contactBirthday.date()
                        << " and calendar displayLabel: " << calendarSummary
                        << " changed details to: " << contact << ", so update the calendar event";
            }

            mCalendar->updateBirthday(contact);
        }
    }
}
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (people-users@projects.maemo.org)
**
** This file is part of contactsd.
**
** If you have questions regarding the use of this file, please contact
** Nokia at people-users@projects.maemo.org.
**
** This library is free software; you can redistribute it and/or
** modify it 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.
**
****************************************************************************/

#include <QTest>
#include <QFile>

#include <QContactFetchByIdRequest>
#include <QContactAvatar>
#include <QContactOnlineAccount>
#include <QContactPhoneNumber>
#include <QContactAddress>
#include <QContactPresence>
#include <QContactEmailAddress>
#include <QContactGlobalPresence>
#include <QContactTag>

#include "test-expectation.h"
#include "debug.h"

// --- TestExpectation ---

void TestExpectation::verify(Event event, const QList<QContactLocalId> &contactIds)
{
     new TestFetchContacts(contactIds, event, this);
}

void TestExpectation::verify(Event event, const QList<QContact> &contacts)
{
    Q_UNUSED(event);
    Q_UNUSED(contacts);

    emitFinished();
}

void TestExpectation::verify(Event event, const QList<QContactLocalId> &contactIds, QContactManager::Error error)
{
    Q_UNUSED(event);
    Q_UNUSED(contactIds);
    Q_UNUSED(error);

    QVERIFY2(false, "Error fetching contacts");
    emitFinished();
}

void TestExpectation::emitFinished()
{
    Q_EMIT finished();
}

// --- TestFetchContacts ---

TestFetchContacts::TestFetchContacts(const QList<QContactLocalId> &contactIds,
        Event event, TestExpectation *exp) : QObject(exp),
        mContactIds(contactIds), mEvent(event), mExp(exp)
{
    QContactFetchByIdRequest *request = new QContactFetchByIdRequest();
    connect(request,
            SIGNAL(resultsAvailable()),
            SLOT(onContactsFetched()));
    request->setManager(mExp->contactManager());
    request->setLocalIds(contactIds);
    QVERIFY(request->start());
}

void TestFetchContacts::onContactsFetched()
{
    QContactFetchByIdRequest *req = qobject_cast<QContactFetchByIdRequest *>(sender());
    if (req == 0 || !req->isFinished()) {
        return;
    }

    if (req->error() == QContactManager::NoError) {
        // For some reason, we get VoiceMail signals sometimes. Ignore them.
        QList<QContact> contacts;
        Q_FOREACH (const QContact &contact, req->contacts()) {
            if (contact.detail<QContactTag>().tag() != QLatin1String("voicemail")) {
                contacts << contact;
            }
        }
        mExp->verify(mEvent, contacts);
    } else {
        mExp->verify(mEvent, mContactIds, req->error());
    }

    deleteLater();
    req->deleteLater();
}

// --- TestExpectationInit ---

void TestExpectationInit::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts[0].localId(), contactManager()->selfContactId());
    emitFinished();
}

// --- TestExpectationCleanup ---

TestExpectationCleanup::TestExpectationCleanup(int nContacts) :
        mNContacts(nContacts), mSelfChanged(false)
{
}

void TestExpectationCleanup::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts[0].localId(), contactManager()->selfContactId());
    mNContacts--;
    mSelfChanged = true;

    maybeEmitFinished();
}

void TestExpectationCleanup::verify(Event event, const QList<QContactLocalId> &contactIds, QContactManager::Error error)
{
    QCOMPARE(event, EventRemoved);
    QCOMPARE(error, QContactManager::DoesNotExistError);
    mNContacts -= contactIds.count();

    maybeEmitFinished();
}

void TestExpectationCleanup::maybeEmitFinished()
{
    QVERIFY(mNContacts >= 0);
    if (mNContacts == 0 && mSelfChanged) {
        emitFinished();
    }
}

// --- TestExpectationContact ---

TestExpectationContact::TestExpectationContact(Event event, QString accountUri):
        mAccountUri(accountUri), mEvent(event), mFlags(0), mPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET),
        mContactInfo(0)
{
}

void TestExpectationContact::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, mEvent);
    QCOMPARE(contacts.count(), 1);
    mContact = contacts[0];
    verify(contacts[0]);
    emitFinished();
}

void TestExpectationContact::verify(Event event, const QList<QContactLocalId> &contactIds, QContactManager::Error error)
{
    QCOMPARE(event, EventRemoved);
    QCOMPARE(contactIds.count(), 1);
    QCOMPARE(error, QContactManager::DoesNotExistError);
    emitFinished();
}

void TestExpectationContact::verify(QContact contact)
{
    debug() << contact;

    if (!mAccountUri.isEmpty()) {
        const QString uri = QString("telepathy:%1!%2").arg(ACCOUNT_PATH).arg(mAccountUri);
        QList<QContactOnlineAccount> details = contact.details<QContactOnlineAccount>("DetailUri", uri);
        QCOMPARE(details.count(), 1);
        QCOMPARE(details[0].value("AccountPath"), QString(ACCOUNT_PATH));
    }

    if (mFlags & VerifyAlias) {
        QString label = contact.detail<QContactDisplayLabel>().label();
        QCOMPARE(label, mAlias);
    }

    if (mFlags & VerifyPresence) {
        QContactPresence::PresenceState presence;
        if (mAccountUri.isEmpty()) {
            QContactGlobalPresence presenceDetail = contact.detail<QContactGlobalPresence>();
            presence = presenceDetail.presenceState();
        } else {
            const QString uri = QString("presence:%1!%2").arg(ACCOUNT_PATH).arg(mAccountUri);
            QList<QContactPresence> details = contact.details<QContactPresence>("DetailUri", uri);
            QCOMPARE(details.count(), 1);
            presence = details[0].presenceState();
        }

        switch (mPresence) {
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE:
            QCOMPARE(presence, QContactPresence::PresenceAvailable);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY:
            QCOMPARE(presence, QContactPresence::PresenceBusy);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_AWAY:
            QCOMPARE(presence, QContactPresence::PresenceAway);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE:
            QCOMPARE(presence, QContactPresence::PresenceOffline);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN:
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_ERROR:
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET:
            QCOMPARE(presence, QContactPresence::PresenceUnknown);
            break;
        }
    }

    if (mFlags & VerifyAvatar) {
        QString avatarFileName = contact.detail<QContactAvatar>().imageUrl().path();
        if (mAvatarData.isEmpty()) {
            QVERIFY2(avatarFileName.isEmpty(), "Expected empty avatar filename");
        } else {
            QFile file(avatarFileName);
            file.open(QIODevice::ReadOnly);
            QCOMPARE(file.readAll(), mAvatarData);
            file.close();
        }
    }

    if (mFlags & VerifyAuthorization) {
        const QString uri = QString("presence:%1!%2").arg(ACCOUNT_PATH).arg(mAccountUri);
        QList<QContactPresence> details = contact.details<QContactPresence>("DetailUri", uri);
        QCOMPARE(details.count(), 1);
        QCOMPARE(details[0].value("AuthStatusFrom"), mSubscriptionState);
        QCOMPARE(details[0].value("AuthStatusTo"), mPublishState);
    }

    if (mFlags & VerifyInfo) {
        uint nMatchedField = 0;

        Q_FOREACH (const QContactDetail &detail, contact.details()) {
            if (detail.definitionName() == "PhoneNumber") {
                QContactPhoneNumber phoneNumber = static_cast<QContactPhoneNumber>(detail);
                verifyContactInfo("tel", QStringList() << phoneNumber.number());
                nMatchedField++;
            }
            else if (detail.definitionName() == "Address") {
                QContactAddress address = static_cast<QContactAddress>(detail);
                verifyContactInfo("adr", QStringList() << address.postOfficeBox()
                                                       << QString("unmapped") // extended address is not mapped
                                                       << address.street()
                                                       << address.locality()
                                                       << address.region()
                                                       << address.postcode()
                                                       << address.country());
                nMatchedField++;
            }
            else if (detail.definitionName() == "EmailAddress") {
                QContactEmailAddress emailAddress = static_cast<QContactEmailAddress >(detail);
                verifyContactInfo("email", QStringList() << emailAddress.emailAddress());
                nMatchedField++;
            }
        }

        if (mContactInfo != NULL) {
            QCOMPARE(nMatchedField, mContactInfo->len);
        }
    }

    if (mFlags & VerifyLocalId) {
        QCOMPARE(contact.localId(), mLocalId);
    }
}

void TestExpectationContact::verifyContactInfo(QString name, const QStringList values) const
{
    QVERIFY2(mContactInfo != NULL, "Found ContactInfo field, was expecting none");

    bool found = false;
    for (uint i = 0; i < mContactInfo->len; i++) {
        gchar *c_name;
        gchar **c_parameters;
        gchar **c_values;

        tp_value_array_unpack((GValueArray*) g_ptr_array_index(mContactInfo, i),
            3, &c_name, &c_parameters, &c_values);

        /* if c_values_len < values.count() it could still be OK if all
         * additional values are empty. */
        gint c_values_len = g_strv_length(c_values);
        if (QString(c_name) != name || c_values_len > values.count()) {
            continue;
        }

        bool match = true;
        for (int j = 0; j < values.count(); j++) {
            if (values[j] == QString("unmapped")) {
                continue;
            }
            if ((j < c_values_len && values[j] != QString(c_values[j])) ||
                (j >= c_values_len && !values[j].isEmpty())) {
                match = false;
                break;
            }
        }

        if (match) {
            found = true;
            break;
        }
    }

    QVERIFY2(found, "Unexpected ContactInfo field");
}

// --- TestExpectationDisconnect ---

TestExpectationDisconnect::TestExpectationDisconnect(int nContacts) :
        TestExpectationContact(EventChanged), mNContacts(nContacts), mSelfChanged(false)
{
}

void TestExpectationDisconnect::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);

    Q_FOREACH (const QContact contact, contacts) {
        if (contact.localId() == contactManager()->selfContactId()) {
            verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE);
            mSelfChanged = true;
        } else {
            verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN);
        }
        TestExpectationContact::verify(contact);
    }

    mNContacts -= contacts.count();
    QVERIFY(mNContacts >= 0);
    if (mNContacts == 0 && mSelfChanged) {
        emitFinished();
    }
}

// --- TestExpectationMerge ---

TestExpectationMerge::TestExpectationMerge(const QContactLocalId masterId,
        const QList<QContactLocalId> mergeIds, const QList<TestExpectationContact *> expectations)
        : mMasterId(masterId), mMergeIds(mergeIds), mGotMergedContact(false),
        mContactExpectations(expectations)
{
}

void TestExpectationMerge::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts[0].localId(), mMasterId);
    mGotMergedContact = true;

    Q_FOREACH (TestExpectationContact *exp, mContactExpectations) {
        exp->verify(contacts[0]);
    }

    maybeEmitFinished();
}

void TestExpectationMerge::verify(Event event, const QList<QContactLocalId> &contactIds,
        QContactManager::Error error)
{
    QCOMPARE(event, EventRemoved);
    QCOMPARE(error, QContactManager::DoesNotExistError);

    Q_FOREACH (QContactLocalId localId, contactIds) {
        QVERIFY(mMergeIds.contains(localId));
        mMergeIds.removeOne(localId);
    }

    maybeEmitFinished();
}

void TestExpectationMerge::maybeEmitFinished()
{
    if (mMergeIds.isEmpty() && mGotMergedContact) {
        emitFinished();
    }
}
<commit_msg>Unit tests: always print fetched contacts and really ignore events for voicemail<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (people-users@projects.maemo.org)
**
** This file is part of contactsd.
**
** If you have questions regarding the use of this file, please contact
** Nokia at people-users@projects.maemo.org.
**
** This library is free software; you can redistribute it and/or
** modify it 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.
**
****************************************************************************/

#include <QTest>
#include <QFile>

#include <QContactFetchByIdRequest>
#include <QContactAvatar>
#include <QContactOnlineAccount>
#include <QContactPhoneNumber>
#include <QContactAddress>
#include <QContactPresence>
#include <QContactEmailAddress>
#include <QContactGlobalPresence>
#include <QContactTag>

#include "test-expectation.h"
#include "debug.h"

// --- TestExpectation ---

void TestExpectation::verify(Event event, const QList<QContactLocalId> &contactIds)
{
     new TestFetchContacts(contactIds, event, this);
}

void TestExpectation::verify(Event event, const QList<QContact> &contacts)
{
    Q_UNUSED(event);
    Q_UNUSED(contacts);

    emitFinished();
}

void TestExpectation::verify(Event event, const QList<QContactLocalId> &contactIds, QContactManager::Error error)
{
    Q_UNUSED(event);
    Q_UNUSED(contactIds);
    Q_UNUSED(error);

    QVERIFY2(false, "Error fetching contacts");
    emitFinished();
}

void TestExpectation::emitFinished()
{
    Q_EMIT finished();
}

// --- TestFetchContacts ---

TestFetchContacts::TestFetchContacts(const QList<QContactLocalId> &contactIds,
        Event event, TestExpectation *exp) : QObject(exp),
        mContactIds(contactIds), mEvent(event), mExp(exp)
{
    QContactFetchByIdRequest *request = new QContactFetchByIdRequest();
    connect(request,
            SIGNAL(resultsAvailable()),
            SLOT(onContactsFetched()));
    request->setManager(mExp->contactManager());
    request->setLocalIds(contactIds);
    QVERIFY(request->start());
}

void TestFetchContacts::onContactsFetched()
{
    QContactFetchByIdRequest *req = qobject_cast<QContactFetchByIdRequest *>(sender());
    if (req == 0 || !req->isFinished()) {
        return;
    }

    if (req->error() == QContactManager::NoError) {
        QList<QContact> contacts;
        Q_FOREACH (const QContact &contact, req->contacts()) {
            debug() << "Contact fetched:\n\n" << contact << "\n";

            // For some reason, we get VoiceMail signals sometimes. Ignore them.
            if (contact.detail<QContactTag>().tag() == QLatin1String("voicemail")) {
                debug() << "Ignoring voicemail contact";
                continue;
            }

            contacts << contact;
        }
        if (!contacts.isEmpty()) {
            mExp->verify(mEvent, contacts);
        }
    } else {
        mExp->verify(mEvent, mContactIds, req->error());
    }

    deleteLater();
    req->deleteLater();
}

// --- TestExpectationInit ---

void TestExpectationInit::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts[0].localId(), contactManager()->selfContactId());
    emitFinished();
}

// --- TestExpectationCleanup ---

TestExpectationCleanup::TestExpectationCleanup(int nContacts) :
        mNContacts(nContacts), mSelfChanged(false)
{
}

void TestExpectationCleanup::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts[0].localId(), contactManager()->selfContactId());
    mNContacts--;
    mSelfChanged = true;

    maybeEmitFinished();
}

void TestExpectationCleanup::verify(Event event, const QList<QContactLocalId> &contactIds, QContactManager::Error error)
{
    QCOMPARE(event, EventRemoved);
    QCOMPARE(error, QContactManager::DoesNotExistError);
    mNContacts -= contactIds.count();

    maybeEmitFinished();
}

void TestExpectationCleanup::maybeEmitFinished()
{
    QVERIFY(mNContacts >= 0);
    if (mNContacts == 0 && mSelfChanged) {
        emitFinished();
    }
}

// --- TestExpectationContact ---

TestExpectationContact::TestExpectationContact(Event event, QString accountUri):
        mAccountUri(accountUri), mEvent(event), mFlags(0), mPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET),
        mContactInfo(0)
{
}

void TestExpectationContact::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, mEvent);
    QCOMPARE(contacts.count(), 1);
    mContact = contacts[0];
    verify(contacts[0]);
    emitFinished();
}

void TestExpectationContact::verify(Event event, const QList<QContactLocalId> &contactIds, QContactManager::Error error)
{
    QCOMPARE(event, EventRemoved);
    QCOMPARE(contactIds.count(), 1);
    QCOMPARE(error, QContactManager::DoesNotExistError);
    emitFinished();
}

void TestExpectationContact::verify(QContact contact)
{
    if (!mAccountUri.isEmpty()) {
        const QString uri = QString("telepathy:%1!%2").arg(ACCOUNT_PATH).arg(mAccountUri);
        QList<QContactOnlineAccount> details = contact.details<QContactOnlineAccount>("DetailUri", uri);
        QCOMPARE(details.count(), 1);
        QCOMPARE(details[0].value("AccountPath"), QString(ACCOUNT_PATH));
    }

    if (mFlags & VerifyAlias) {
        QString label = contact.detail<QContactDisplayLabel>().label();
        QCOMPARE(label, mAlias);
    }

    if (mFlags & VerifyPresence) {
        QContactPresence::PresenceState presence;
        if (mAccountUri.isEmpty()) {
            QContactGlobalPresence presenceDetail = contact.detail<QContactGlobalPresence>();
            presence = presenceDetail.presenceState();
        } else {
            const QString uri = QString("presence:%1!%2").arg(ACCOUNT_PATH).arg(mAccountUri);
            QList<QContactPresence> details = contact.details<QContactPresence>("DetailUri", uri);
            QCOMPARE(details.count(), 1);
            presence = details[0].presenceState();
        }

        switch (mPresence) {
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE:
            QCOMPARE(presence, QContactPresence::PresenceAvailable);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY:
            QCOMPARE(presence, QContactPresence::PresenceBusy);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_AWAY:
            QCOMPARE(presence, QContactPresence::PresenceAway);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE:
            QCOMPARE(presence, QContactPresence::PresenceOffline);
            break;
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN:
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_ERROR:
        case TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET:
            QCOMPARE(presence, QContactPresence::PresenceUnknown);
            break;
        }
    }

    if (mFlags & VerifyAvatar) {
        QString avatarFileName = contact.detail<QContactAvatar>().imageUrl().path();
        if (mAvatarData.isEmpty()) {
            QVERIFY2(avatarFileName.isEmpty(), "Expected empty avatar filename");
        } else {
            QFile file(avatarFileName);
            file.open(QIODevice::ReadOnly);
            QCOMPARE(file.readAll(), mAvatarData);
            file.close();
        }
    }

    if (mFlags & VerifyAuthorization) {
        const QString uri = QString("presence:%1!%2").arg(ACCOUNT_PATH).arg(mAccountUri);
        QList<QContactPresence> details = contact.details<QContactPresence>("DetailUri", uri);
        QCOMPARE(details.count(), 1);
        QCOMPARE(details[0].value("AuthStatusFrom"), mSubscriptionState);
        QCOMPARE(details[0].value("AuthStatusTo"), mPublishState);
    }

    if (mFlags & VerifyInfo) {
        uint nMatchedField = 0;

        Q_FOREACH (const QContactDetail &detail, contact.details()) {
            if (detail.definitionName() == "PhoneNumber") {
                QContactPhoneNumber phoneNumber = static_cast<QContactPhoneNumber>(detail);
                verifyContactInfo("tel", QStringList() << phoneNumber.number());
                nMatchedField++;
            }
            else if (detail.definitionName() == "Address") {
                QContactAddress address = static_cast<QContactAddress>(detail);
                verifyContactInfo("adr", QStringList() << address.postOfficeBox()
                                                       << QString("unmapped") // extended address is not mapped
                                                       << address.street()
                                                       << address.locality()
                                                       << address.region()
                                                       << address.postcode()
                                                       << address.country());
                nMatchedField++;
            }
            else if (detail.definitionName() == "EmailAddress") {
                QContactEmailAddress emailAddress = static_cast<QContactEmailAddress >(detail);
                verifyContactInfo("email", QStringList() << emailAddress.emailAddress());
                nMatchedField++;
            }
        }

        if (mContactInfo != NULL) {
            QCOMPARE(nMatchedField, mContactInfo->len);
        }
    }

    if (mFlags & VerifyLocalId) {
        QCOMPARE(contact.localId(), mLocalId);
    }
}

void TestExpectationContact::verifyContactInfo(QString name, const QStringList values) const
{
    QVERIFY2(mContactInfo != NULL, "Found ContactInfo field, was expecting none");

    bool found = false;
    for (uint i = 0; i < mContactInfo->len; i++) {
        gchar *c_name;
        gchar **c_parameters;
        gchar **c_values;

        tp_value_array_unpack((GValueArray*) g_ptr_array_index(mContactInfo, i),
            3, &c_name, &c_parameters, &c_values);

        /* if c_values_len < values.count() it could still be OK if all
         * additional values are empty. */
        gint c_values_len = g_strv_length(c_values);
        if (QString(c_name) != name || c_values_len > values.count()) {
            continue;
        }

        bool match = true;
        for (int j = 0; j < values.count(); j++) {
            if (values[j] == QString("unmapped")) {
                continue;
            }
            if ((j < c_values_len && values[j] != QString(c_values[j])) ||
                (j >= c_values_len && !values[j].isEmpty())) {
                match = false;
                break;
            }
        }

        if (match) {
            found = true;
            break;
        }
    }

    QVERIFY2(found, "Unexpected ContactInfo field");
}

// --- TestExpectationDisconnect ---

TestExpectationDisconnect::TestExpectationDisconnect(int nContacts) :
        TestExpectationContact(EventChanged), mNContacts(nContacts), mSelfChanged(false)
{
}

void TestExpectationDisconnect::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);

    Q_FOREACH (const QContact contact, contacts) {
        if (contact.localId() == contactManager()->selfContactId()) {
            verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE);
            mSelfChanged = true;
        } else {
            verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN);
        }
        TestExpectationContact::verify(contact);
    }

    mNContacts -= contacts.count();
    QVERIFY(mNContacts >= 0);
    if (mNContacts == 0 && mSelfChanged) {
        emitFinished();
    }
}

// --- TestExpectationMerge ---

TestExpectationMerge::TestExpectationMerge(const QContactLocalId masterId,
        const QList<QContactLocalId> mergeIds, const QList<TestExpectationContact *> expectations)
        : mMasterId(masterId), mMergeIds(mergeIds), mGotMergedContact(false),
        mContactExpectations(expectations)
{
}

void TestExpectationMerge::verify(Event event, const QList<QContact> &contacts)
{
    QCOMPARE(event, EventChanged);
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts[0].localId(), mMasterId);
    mGotMergedContact = true;

    Q_FOREACH (TestExpectationContact *exp, mContactExpectations) {
        exp->verify(contacts[0]);
    }

    maybeEmitFinished();
}

void TestExpectationMerge::verify(Event event, const QList<QContactLocalId> &contactIds,
        QContactManager::Error error)
{
    QCOMPARE(event, EventRemoved);
    QCOMPARE(error, QContactManager::DoesNotExistError);

    Q_FOREACH (QContactLocalId localId, contactIds) {
        QVERIFY(mMergeIds.contains(localId));
        mMergeIds.removeOne(localId);
    }

    maybeEmitFinished();
}

void TestExpectationMerge::maybeEmitFinished()
{
    if (mMergeIds.isEmpty() && mGotMergedContact) {
        emitFinished();
    }
}
<|endoftext|>


<commit_before>#include <SDL2/SDL.h>

#include "client.h"

client::client() :
    m_mouseLat(0.0f),
    m_mouseLon(0.0f),
    m_origin(0.0f, 150.0f, 0.0f),
    m_velocity(0.0f, 0.0f, 0.0f),
    m_isOnGround(false),
    m_isOnWall(false)
{

}

u::map<int, int> &getKeyState(int key = 0, bool keyDown = false, bool keyUp = false);
void getMouseDelta(int *deltaX, int *deltaY);

bool client::tryUnstick(const kdMap &map, float radius) {
    static const m::vec3 offsets[] = {
        m::vec3(-1.0f, 1.0f,-1.0f),
        m::vec3( 1.0f, 1.0f,-1.0f),
        m::vec3( 1.0f, 1.0f, 1.0f),
        m::vec3(-1.0f, 1.0f, 1.0f),
        m::vec3(-1.0f,-1.0f,-1.0f),
        m::vec3( 1.0f,-1.0f,-1.0f),
        m::vec3( 1.0f,-1.0f, 1.0f),
        m::vec3(-1.0f,-1.0f, 1.0f)
    };
    const float radiusScale = radius * 0.1f;
    for (size_t j = 1; j < 4; j++) {
        for (size_t i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++) {
            m::vec3 tryPosition = m_origin + j * radiusScale * offsets[i];
            if (map.isSphereStuck(tryPosition, radius))
                continue;
            m_origin = tryPosition;
            return true;
        }
    }
    return false;
}

void client::update(const kdMap &map, float dt) {
    static constexpr float kMaxVelocity = 80.0f;
    static constexpr m::vec3 kGravity(0.0f, -98.0f, 0.0f);
    static constexpr float kRadius = 5.0f;

    kdSphereTrace trace;
    trace.radius = kRadius;

    m::vec3 velocity = m_velocity;
    m::vec3 originalVelocity = m_velocity;
    m::vec3 primalVelocity = m_velocity;
    m::vec3 newVelocity;
    velocity.maxLength(kMaxVelocity);

    m::vec3 planes[kdMap::kMaxClippingPlanes];
    m::vec3 pos = m_origin;
    float timeLeft = dt;
    float allFraction = 0.0f;
    size_t numBumps = kdMap::kMaxBumps;
    size_t numPlanes = 0;

    bool wallHit = false;
    bool groundHit = false;
    for (size_t bumpCount = 0; bumpCount < numBumps; bumpCount++) {
        if (velocity == m::vec3(0.0f, 0.0f, 0.0f))
            break;

        trace.start = pos;
        trace.dir = timeLeft * velocity;
        map.traceSphere(&trace);

        float f = m::clamp(trace.fraction, 0.0f, 1.0f);
        allFraction += f;

        if (f > 0.0f) {
            pos += trace.dir * f * kdMap::kFractionScale;
            originalVelocity = velocity;
            numPlanes = 0;
        }

        if (f == 1.0f)
            break;

        wallHit = true;

        timeLeft = timeLeft * (1.0f - f);

        if (trace.plane.n[1] > 0.7f)
            groundHit = true;

        if (numPlanes >= kdMap::kMaxClippingPlanes) {
            velocity = m::vec3(0.0f, 0.0f, 0.0f);
            break;
        }

        // next clipping plane
        planes[numPlanes++] = trace.plane.n;

        size_t i, j;
        for (i = 0; i < numPlanes; i++) {
            kdMap::clipVelocity(originalVelocity, planes[i], newVelocity, kdMap::kOverClip);
            for (j = 0; j < numPlanes; j++)
                if (j != i && !(planes[i] == planes[j]))
                    if (newVelocity * planes[j] < 0.0f)
                        break;
            if (j == numPlanes)
                break;
        }

        // did we make it through the entire plane set?
        if (i != numPlanes)
            velocity = newVelocity;
        else {
            // go along the planes crease
            if (numPlanes != 2) {
                velocity = m::vec3(0.0f, 0.0f, 0.0f);
                break;
            }
            m::vec3 dir = planes[0] ^ planes[1];
            float d = dir * velocity;
            velocity = dir * d;
        }

        if (velocity * primalVelocity <= 0.0f) {
            velocity = m::vec3(0.0f, 0.0f, 0.0f);
            break;
        }
    }

    if (allFraction == 0.0f) {
        velocity = m::vec3(0.0f, 0.0f, 0.0f);
    }

    m_isOnGround = groundHit;
    m_isOnWall = wallHit;
    if (groundHit) {
        velocity.y = 0.0f;
    } else {
        velocity += kGravity * dt;
    }

    // we could be stuck
    if (map.isSphereStuck(pos, kRadius)) {
        m_origin = pos;
        tryUnstick(map, kRadius);
    }

    m_origin = pos;
    m_velocity = velocity;

    u::vector<clientCommands> commands;
    inputGetCommands(commands);
    inputMouseMove();

    move(commands);
}

void client::move(const u::vector<clientCommands> &commands) {
    m::vec3 velocity = m_velocity;
    m::vec3 direction;
    m::vec3 side;
    m::vec3 up;
    m::vec3 newDirection(0.0f, 0.0f, 0.0f);
    m::vec3 jump(0.0f, 0.0f, 0.0f);
    m::quat rotation = m_rotation;

    rotation.getOrient(&direction, &up, &side);

    // at half of the 45 degrees in either direction invert the sign
    // We do it between two points to prevent a situation where the
    // camera is just at the right axis thus preventing movement.
    up = (m::toDegree(direction.y) > 45.0f / 2.0f) ? -up : up;
    m::vec3 upCamera;
    for (auto &it : commands) {
        switch (it) {
            case kCommandForward:
                newDirection += direction + up;
                break;
            case kCommandBackward:
                newDirection -= direction + up;
                break;
            case kCommandLeft:
                newDirection -= side;
                break;
            case kCommandRight:
                newDirection += side;
                break;
            case kCommandJump:
                jump = m::vec3(0.0f, 0.25f, 0.0f);
                break;
        }
    }

    const float kClientSpeed = 60.0f; // cm/s
    const float kJumpSpeed = 130.0f; // cm/s
    const float kStopSpeed = 90.0f; // -cm/s

    newDirection.y = 0.0f;
    if (newDirection.absSquared() > 0.1f)
        newDirection.setLength(kClientSpeed);
    newDirection.y += velocity.y;
    if (m_isOnGround) {
        newDirection += jump * kJumpSpeed;
    }
    if (commands.size() == 0) {
        m::vec3 slowDown = m_velocity * kStopSpeed * 0.01f;
        slowDown.y = 0.0f;
        newDirection += slowDown;
    }
    m_lastDirection = direction;
    m_velocity = newDirection;
}

void client::inputMouseMove(void) {
    static const float kSensitivity = 0.50f / 6.0f;
    static const bool kInvert = true;
    const float invert = kInvert ? -1.0f : 1.0f;

    int deltaX;
    int deltaY;
    getMouseDelta(&deltaX, &deltaY);

    m_mouseLat -= (float)deltaY * kSensitivity * invert;
    m_mouseLat = m::clamp(m_mouseLat, -89.0f, 89.0f);

    m_mouseLon -= (float)deltaX * kSensitivity * invert;
    m_mouseLon = m::angleMod(m_mouseLon);

    m::quat qlat(m::vec3::xAxis, m_mouseLat * m::kDegToRad);
    m::quat qlon(m::vec3::yAxis, m_mouseLon * m::kDegToRad);

    setRotation(qlon * qlat);
}

void client::inputGetCommands(u::vector<clientCommands> &commands) {
    u::map<int, int> &keyState = getKeyState();
    commands.clear();
    if (keyState[SDLK_w])     commands.push_back(kCommandForward);
    if (keyState[SDLK_s])     commands.push_back(kCommandBackward);
    if (keyState[SDLK_a])     commands.push_back(kCommandLeft);
    if (keyState[SDLK_d])     commands.push_back(kCommandRight);
    if (keyState[SDLK_SPACE]) commands.push_back(kCommandJump);
}

void client::setRotation(const m::quat &rotation) {
    m_rotation = rotation;
}

void client::getDirection(m::vec3 *direction, m::vec3 *up, m::vec3 *side) const {
    return m_rotation.getOrient(direction, up, side);
}

const m::quat &client::getRotation(void) const {
    return m_rotation;
}

m::vec3 client::getPosition(void) const {
    // adjust for eye height
    return m::vec3(m_origin.x, m_origin.y + 5.5f, m_origin.z);
}
<commit_msg>use the to functions instead<commit_after>#include <SDL2/SDL.h>

#include "client.h"

client::client() :
    m_mouseLat(0.0f),
    m_mouseLon(0.0f),
    m_origin(0.0f, 150.0f, 0.0f),
    m_velocity(0.0f, 0.0f, 0.0f),
    m_isOnGround(false),
    m_isOnWall(false)
{

}

u::map<int, int> &getKeyState(int key = 0, bool keyDown = false, bool keyUp = false);
void getMouseDelta(int *deltaX, int *deltaY);

bool client::tryUnstick(const kdMap &map, float radius) {
    static const m::vec3 offsets[] = {
        m::vec3(-1.0f, 1.0f,-1.0f),
        m::vec3( 1.0f, 1.0f,-1.0f),
        m::vec3( 1.0f, 1.0f, 1.0f),
        m::vec3(-1.0f, 1.0f, 1.0f),
        m::vec3(-1.0f,-1.0f,-1.0f),
        m::vec3( 1.0f,-1.0f,-1.0f),
        m::vec3( 1.0f,-1.0f, 1.0f),
        m::vec3(-1.0f,-1.0f, 1.0f)
    };
    const float radiusScale = radius * 0.1f;
    for (size_t j = 1; j < 4; j++) {
        for (size_t i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++) {
            m::vec3 tryPosition = m_origin + j * radiusScale * offsets[i];
            if (map.isSphereStuck(tryPosition, radius))
                continue;
            m_origin = tryPosition;
            return true;
        }
    }
    return false;
}

void client::update(const kdMap &map, float dt) {
    static constexpr float kMaxVelocity = 80.0f;
    static constexpr m::vec3 kGravity(0.0f, -98.0f, 0.0f);
    static constexpr float kRadius = 5.0f;

    kdSphereTrace trace;
    trace.radius = kRadius;

    m::vec3 velocity = m_velocity;
    m::vec3 originalVelocity = m_velocity;
    m::vec3 primalVelocity = m_velocity;
    m::vec3 newVelocity;
    velocity.maxLength(kMaxVelocity);

    m::vec3 planes[kdMap::kMaxClippingPlanes];
    m::vec3 pos = m_origin;
    float timeLeft = dt;
    float allFraction = 0.0f;
    size_t numBumps = kdMap::kMaxBumps;
    size_t numPlanes = 0;

    bool wallHit = false;
    bool groundHit = false;
    for (size_t bumpCount = 0; bumpCount < numBumps; bumpCount++) {
        if (velocity == m::vec3(0.0f, 0.0f, 0.0f))
            break;

        trace.start = pos;
        trace.dir = timeLeft * velocity;
        map.traceSphere(&trace);

        float f = m::clamp(trace.fraction, 0.0f, 1.0f);
        allFraction += f;

        if (f > 0.0f) {
            pos += trace.dir * f * kdMap::kFractionScale;
            originalVelocity = velocity;
            numPlanes = 0;
        }

        if (f == 1.0f)
            break;

        wallHit = true;

        timeLeft = timeLeft * (1.0f - f);

        if (trace.plane.n[1] > 0.7f)
            groundHit = true;

        if (numPlanes >= kdMap::kMaxClippingPlanes) {
            velocity = m::vec3(0.0f, 0.0f, 0.0f);
            break;
        }

        // next clipping plane
        planes[numPlanes++] = trace.plane.n;

        size_t i, j;
        for (i = 0; i < numPlanes; i++) {
            kdMap::clipVelocity(originalVelocity, planes[i], newVelocity, kdMap::kOverClip);
            for (j = 0; j < numPlanes; j++)
                if (j != i && !(planes[i] == planes[j]))
                    if (newVelocity * planes[j] < 0.0f)
                        break;
            if (j == numPlanes)
                break;
        }

        // did we make it through the entire plane set?
        if (i != numPlanes)
            velocity = newVelocity;
        else {
            // go along the planes crease
            if (numPlanes != 2) {
                velocity = m::vec3(0.0f, 0.0f, 0.0f);
                break;
            }
            m::vec3 dir = planes[0] ^ planes[1];
            float d = dir * velocity;
            velocity = dir * d;
        }

        if (velocity * primalVelocity <= 0.0f) {
            velocity = m::vec3(0.0f, 0.0f, 0.0f);
            break;
        }
    }

    if (allFraction == 0.0f) {
        velocity = m::vec3(0.0f, 0.0f, 0.0f);
    }

    m_isOnGround = groundHit;
    m_isOnWall = wallHit;
    if (groundHit) {
        velocity.y = 0.0f;
    } else {
        velocity += kGravity * dt;
    }

    // we could be stuck
    if (map.isSphereStuck(pos, kRadius)) {
        m_origin = pos;
        tryUnstick(map, kRadius);
    }

    m_origin = pos;
    m_velocity = velocity;

    u::vector<clientCommands> commands;
    inputGetCommands(commands);
    inputMouseMove();

    move(commands);
}

void client::move(const u::vector<clientCommands> &commands) {
    m::vec3 velocity = m_velocity;
    m::vec3 direction;
    m::vec3 side;
    m::vec3 up;
    m::vec3 newDirection(0.0f, 0.0f, 0.0f);
    m::vec3 jump(0.0f, 0.0f, 0.0f);
    m::quat rotation = m_rotation;

    rotation.getOrient(&direction, &up, &side);

    // at half of the 45 degrees in either direction invert the sign
    // We do it between two points to prevent a situation where the
    // camera is just at the right axis thus preventing movement.
    up = (m::toDegree(direction.y) > 45.0f / 2.0f) ? -up : up;
    m::vec3 upCamera;
    for (auto &it : commands) {
        switch (it) {
            case kCommandForward:
                newDirection += direction + up;
                break;
            case kCommandBackward:
                newDirection -= direction + up;
                break;
            case kCommandLeft:
                newDirection -= side;
                break;
            case kCommandRight:
                newDirection += side;
                break;
            case kCommandJump:
                jump = m::vec3(0.0f, 0.25f, 0.0f);
                break;
        }
    }

    const float kClientSpeed = 60.0f; // cm/s
    const float kJumpSpeed = 130.0f; // cm/s
    const float kStopSpeed = 90.0f; // -cm/s

    newDirection.y = 0.0f;
    if (newDirection.absSquared() > 0.1f)
        newDirection.setLength(kClientSpeed);
    newDirection.y += velocity.y;
    if (m_isOnGround) {
        newDirection += jump * kJumpSpeed;
    }
    if (commands.size() == 0) {
        m::vec3 slowDown = m_velocity * kStopSpeed * 0.01f;
        slowDown.y = 0.0f;
        newDirection += slowDown;
    }
    m_lastDirection = direction;
    m_velocity = newDirection;
}

void client::inputMouseMove(void) {
    static const float kSensitivity = 0.50f / 6.0f;
    static const bool kInvert = true;
    const float invert = kInvert ? -1.0f : 1.0f;

    int deltaX;
    int deltaY;
    getMouseDelta(&deltaX, &deltaY);

    m_mouseLat -= (float)deltaY * kSensitivity * invert;
    m_mouseLat = m::clamp(m_mouseLat, -89.0f, 89.0f);

    m_mouseLon -= (float)deltaX * kSensitivity * invert;
    m_mouseLon = m::angleMod(m_mouseLon);

    m::quat qlat(m::vec3::xAxis, m::toRadian(m_mouseLat));
    m::quat qlon(m::vec3::yAxis, m::toRadian(m_mouseLon));

    setRotation(qlon * qlat);
}

void client::inputGetCommands(u::vector<clientCommands> &commands) {
    u::map<int, int> &keyState = getKeyState();
    commands.clear();
    if (keyState[SDLK_w])     commands.push_back(kCommandForward);
    if (keyState[SDLK_s])     commands.push_back(kCommandBackward);
    if (keyState[SDLK_a])     commands.push_back(kCommandLeft);
    if (keyState[SDLK_d])     commands.push_back(kCommandRight);
    if (keyState[SDLK_SPACE]) commands.push_back(kCommandJump);
}

void client::setRotation(const m::quat &rotation) {
    m_rotation = rotation;
}

void client::getDirection(m::vec3 *direction, m::vec3 *up, m::vec3 *side) const {
    return m_rotation.getOrient(direction, up, side);
}

const m::quat &client::getRotation(void) const {
    return m_rotation;
}

m::vec3 client::getPosition(void) const {
    // adjust for eye height
    return m::vec3(m_origin.x, m_origin.y + 5.5f, m_origin.z);
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2013, Project OSRM, Dennis Luxen, others
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include "DouglasPeucker.h"

#include "../DataStructures/Range.h"
#include "../DataStructures/SegmentInformation.h"

#include <osrm/Coordinate.h>

#include <boost/assert.hpp>

#include <cmath>

#include <algorithm>

namespace
{
struct CoordinatePairCalculator
{
    CoordinatePairCalculator() = delete;
    CoordinatePairCalculator(const FixedPointCoordinate &coordinate_a,
                             const FixedPointCoordinate &coordinate_b)
    {
        // initialize distance calculator with two fixed coordinates a, b
        const float RAD = 0.017453292519943295769236907684886f;
        first_lat = (coordinate_a.lat / COORDINATE_PRECISION) * RAD;
        first_lon = (coordinate_a.lon / COORDINATE_PRECISION) * RAD;
        second_lat = (coordinate_b.lat / COORDINATE_PRECISION) * RAD;
        second_lon = (coordinate_b.lon / COORDINATE_PRECISION) * RAD;
    }

    int operator()(FixedPointCoordinate &other) const
    {
        // set third coordinate c
        const float RAD = 0.017453292519943295769236907684886f;
        const float earth_radius = 6372797.560856f;
        const float float_lat1 = (other.lat / COORDINATE_PRECISION) * RAD;
        const float float_lon1 = (other.lon / COORDINATE_PRECISION) * RAD;

        // compute distance (a,c)
        const float x_value_1 = (first_lon - float_lon1) * cos((float_lat1 + first_lat) / 2.f);
        const float y_value_1 = first_lat - float_lat1;
        const float dist1 = sqrt(std::pow(x_value_1, 2) + std::pow(y_value_1, 2)) * earth_radius;

        // compute distance (b,c)
        const float x_value_2 = (second_lon - float_lon1) * cos((float_lat1 + second_lat) / 2.f);
        const float y_value_2 = second_lat - float_lat1;
        const float dist2 = sqrt(std::pow(x_value_2, 2) + std::pow(y_value_2, 2)) * earth_radius;

        // return the minimum
        return static_cast<int>(std::min(dist1, dist2));
    }

    float first_lat;
    float first_lon;
    float second_lat;
    float second_lon;
};
}

void DouglasPeucker::Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level)
{
    Run(std::begin(input_geometry), std::end(input_geometry), zoom_level);
}

void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level)
{
    unsigned size = std::distance(begin, end);
    if (size < 2)
    {
        return;
    }

    begin->necessary = true;
    std::prev(end)->necessary = true;

    {
        BOOST_ASSERT_MSG(zoom_level < 19, "unsupported zoom level");
        RandomAccessIt left_border = begin;
        RandomAccessIt right_border = std::next(begin);
        // Sweep over array and identify those ranges that need to be checked
        do
        {
            // traverse list until new border element found
            if (right_border->necessary)
            {
                // sanity checks
                BOOST_ASSERT(left_border->necessary);
                BOOST_ASSERT(right_border->necessary);
                recursion_stack.emplace(left_border, right_border);
                left_border = right_border;
            }
            ++right_border;
        } while (right_border != end);
    }

    // mark locations as 'necessary' by divide-and-conquer
    while (!recursion_stack.empty())
    {
        // pop next element
        const GeometryRange pair = recursion_stack.top();
        recursion_stack.pop();
        // sanity checks
        BOOST_ASSERT_MSG(pair.first->necessary, "left border must be necessary");
        BOOST_ASSERT_MSG(pair.second->necessary, "right border must be necessary");
        BOOST_ASSERT_MSG(std::distance(pair.second, end) > 0, "right border outside of geometry");
        BOOST_ASSERT_MSG(std::distance(pair.first, pair.second) >= 0, "left border on the wrong side");

        int max_int_distance = 0;
        auto farthest_entry_it = pair.second;
        const CoordinatePairCalculator dist_calc(pair.first->location, pair.second->location);

        // sweep over range to find the maximum
        for (auto it = std::next(pair.first); it != pair.second; ++it)
        {
            const int distance = dist_calc(it->location);
            // found new feasible maximum?
            if (distance > max_int_distance && distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
            {
                farthest_entry_it = it;
                max_int_distance = distance;
            }
        }

        // check if maximum violates a zoom level dependent threshold
        if (max_int_distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
        {
            //  mark idx as necessary
            farthest_entry_it->necessary = true;
            if (1 < std::distance(pair.first, farthest_entry_it))
            {
                recursion_stack.emplace(pair.first, farthest_entry_it);
            }
            if (1 < std::distance(farthest_entry_it, pair.second))
            {
                recursion_stack.emplace(farthest_entry_it, pair.second);
            }
        }
    }
}
<commit_msg>reformatting<commit_after>/*

Copyright (c) 2013, Project OSRM, Dennis Luxen, others
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include "DouglasPeucker.h"

#include "../DataStructures/Range.h"
#include "../DataStructures/SegmentInformation.h"

#include <osrm/Coordinate.h>

#include <boost/assert.hpp>

#include <cmath>

#include <algorithm>

namespace
{
struct CoordinatePairCalculator
{
    CoordinatePairCalculator() = delete;
    CoordinatePairCalculator(const FixedPointCoordinate &coordinate_a,
                             const FixedPointCoordinate &coordinate_b)
    {
        // initialize distance calculator with two fixed coordinates a, b
        const float RAD = 0.017453292519943295769236907684886f;
        first_lat = (coordinate_a.lat / COORDINATE_PRECISION) * RAD;
        first_lon = (coordinate_a.lon / COORDINATE_PRECISION) * RAD;
        second_lat = (coordinate_b.lat / COORDINATE_PRECISION) * RAD;
        second_lon = (coordinate_b.lon / COORDINATE_PRECISION) * RAD;
    }

    int operator()(FixedPointCoordinate &other) const
    {
        // set third coordinate c
        const float RAD = 0.017453292519943295769236907684886f;
        const float earth_radius = 6372797.560856f;
        const float float_lat1 = (other.lat / COORDINATE_PRECISION) * RAD;
        const float float_lon1 = (other.lon / COORDINATE_PRECISION) * RAD;

        // compute distance (a,c)
        const float x_value_1 = (first_lon - float_lon1) * cos((float_lat1 + first_lat) / 2.f);
        const float y_value_1 = first_lat - float_lat1;
        const float dist1 = sqrt(std::pow(x_value_1, 2) + std::pow(y_value_1, 2)) * earth_radius;

        // compute distance (b,c)
        const float x_value_2 = (second_lon - float_lon1) * cos((float_lat1 + second_lat) / 2.f);
        const float y_value_2 = second_lat - float_lat1;
        const float dist2 = sqrt(std::pow(x_value_2, 2) + std::pow(y_value_2, 2)) * earth_radius;

        // return the minimum
        return static_cast<int>(std::min(dist1, dist2));
    }

    float first_lat;
    float first_lon;
    float second_lat;
    float second_lon;
};
}

void DouglasPeucker::Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level)
{
    Run(std::begin(input_geometry), std::end(input_geometry), zoom_level);
}

void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level)
{
    unsigned size = std::distance(begin, end);
    if (size < 2)
    {
        return;
    }

    begin->necessary = true;
    std::prev(end)->necessary = true;

    {
        BOOST_ASSERT_MSG(zoom_level < 19, "unsupported zoom level");
        RandomAccessIt left_border = begin;
        RandomAccessIt right_border = std::next(begin);
        // Sweep over array and identify those ranges that need to be checked
        do
        {
            // traverse list until new border element found
            if (right_border->necessary)
            {
                // sanity checks
                BOOST_ASSERT(left_border->necessary);
                BOOST_ASSERT(right_border->necessary);
                recursion_stack.emplace(left_border, right_border);
                left_border = right_border;
            }
            ++right_border;
        } while (right_border != end);
    }

    // mark locations as 'necessary' by divide-and-conquer
    while (!recursion_stack.empty())
    {
        // pop next element
        const GeometryRange pair = recursion_stack.top();
        recursion_stack.pop();
        // sanity checks
        BOOST_ASSERT_MSG(pair.first->necessary, "left border must be necessary");
        BOOST_ASSERT_MSG(pair.second->necessary, "right border must be necessary");
        BOOST_ASSERT_MSG(std::distance(pair.second, end) > 0, "right border outside of geometry");
        BOOST_ASSERT_MSG(std::distance(pair.first, pair.second) >= 0,
                         "left border on the wrong side");

        int max_int_distance = 0;
        auto farthest_entry_it = pair.second;
        const CoordinatePairCalculator dist_calc(pair.first->location, pair.second->location);

        // sweep over range to find the maximum
        for (auto it = std::next(pair.first); it != pair.second; ++it)
        {
            const int distance = dist_calc(it->location);
            // found new feasible maximum?
            if (distance > max_int_distance && distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
            {
                farthest_entry_it = it;
                max_int_distance = distance;
            }
        }

        // check if maximum violates a zoom level dependent threshold
        if (max_int_distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
        {
            //  mark idx as necessary
            farthest_entry_it->necessary = true;
            if (1 < std::distance(pair.first, farthest_entry_it))
            {
                recursion_stack.emplace(pair.first, farthest_entry_it);
            }
            if (1 < std::distance(farthest_entry_it, pair.second))
            {
                recursion_stack.emplace(farthest_entry_it, pair.second);
            }
        }
    }
}
<|endoftext|>


<commit_before>#include "dafs/node.hpp"

namespace dafs
{
    Node::Node()
        : slot_minus(dafs::Root("p-minus")),
          slot_zero(dafs::Root("p-zero")),
          slot_plus(dafs::Root("p-plus"))
    {
    }

    dafs::Partition&
    Node::GetPartition(Slot slot)
    {
        switch (slot)
        {
            case Slot::Minus:
            {
                return slot_minus;
            }
            case Slot::Zero:
            {
                return slot_zero;
            }
            case Slot::Plus:
            {
                return slot_plus;
            }
        }
    }

    dafs::Partition&
    Node::GetPartition(Identity identity)
    {
        return slot_zero;
    }
}
<commit_msg>Implement identity to partition association<commit_after>#include "dafs/node.hpp"

namespace dafs
{
    Node::Node()
        : slot_minus(dafs::Root("p-minus")),
          slot_zero(dafs::Root("p-zero")),
          slot_plus(dafs::Root("p-plus"))
    {
    }

    dafs::Partition&
    Node::GetPartition(Slot slot)
    {
        switch (slot)
        {
            case Slot::Minus:
            {
                return slot_minus;
            }
            case Slot::Zero:
            {
                return slot_zero;
            }
            case Slot::Plus:
            {
                return slot_plus;
            }
        }
    }

    dafs::Partition&
    Node::GetPartition(Identity identity)
    {
        if (identity < slot_minus.GetIdentity())
        {
            return slot_minus;
        }
        else if (slot_plus.GetIdentity() < identity)
        {
            return slot_plus;
        }
        else
        {
            return slot_zero;
        }
    }
}
<|endoftext|>