/*
* This file is part of TelepathyQt4
*
* Copyright (C) 2008 Collabora Ltd.
* Copyright (C) 2008 Nokia Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include
#include "TelepathyQt4/_gen/pending-handles.moc.hpp"
#include
#include
#include "TelepathyQt4/debug-internal.h"
namespace Tp
{
struct TELEPATHY_QT4_NO_EXPORT PendingHandles::Private
{
HandleType handleType;
bool isRequest;
QStringList namesRequested;
UIntList handlesToReference;
ReferencedHandles handles;
ReferencedHandles alreadyHeld;
UIntList invalidHandles;
QStringList validNames;
QHash > invalidNames;
// one to one requests (ids)
QHash handlesForWatchers;
QHash idsForWatchers;
QHash handlesForIds;
int requestsFinished;
};
/**
* \class PendingHandles
* \ingroup clientconn
* \headerfile TelepathyQt4/pending-handles.h
*
* \brief Class containing the parameters of and the reply to an asynchronous
* handle request/hold. Instances of this class cannot be constructed directly;
* the only way to get one is to use Connection::requestHandles() or
* Connection::referenceHandles().
*/
PendingHandles::PendingHandles(const ConnectionPtr &connection, HandleType handleType,
const QStringList &names)
: PendingOperation(connection),
mPriv(new Private)
{
debug() << "PendingHandles(request)";
mPriv->handleType = handleType;
mPriv->isRequest = true;
mPriv->namesRequested = names;
mPriv->requestsFinished = 0;
// try to request all handles at once
QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(
connection->baseInterface()->RequestHandles(mPriv->handleType, names),
this);
connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher*)),
SLOT(onRequestHandlesFinished(QDBusPendingCallWatcher*)));
}
PendingHandles::PendingHandles(const ConnectionPtr &connection, HandleType handleType,
const UIntList &handles, const UIntList &alreadyHeld,
const UIntList ¬YetHeld)
: PendingOperation(connection),
mPriv(new Private)
{
debug() << "PendingHandles(reference)";
mPriv->handleType = handleType;
mPriv->isRequest = false;
mPriv->handlesToReference = handles;
mPriv->alreadyHeld = ReferencedHandles(connection, mPriv->handleType, alreadyHeld);
mPriv->requestsFinished = 0;
if (notYetHeld.isEmpty()) {
debug() << " All handles already held, finishing up instantly";
mPriv->handles = mPriv->alreadyHeld;
setFinished();
} else {
debug() << " Calling HoldHandles";
QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(
connection->baseInterface()->HoldHandles(mPriv->handleType, notYetHeld),
this);
connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher*)),
SLOT(onHoldHandlesFinished(QDBusPendingCallWatcher*)));
}
}
PendingHandles::PendingHandles(const QString &errorName, const QString &errorMessage)
: PendingOperation(ConnectionPtr()), mPriv(new Private)
{
setFinishedWithError(errorName, errorMessage);
}
/**
* Class destructor.
*/
PendingHandles::~PendingHandles()
{
delete mPriv;
}
/**
* Returns the Connection object through which the operation was made.
*
* \return Pointer to the Connection.
*/
ConnectionPtr PendingHandles::connection() const
{
return ConnectionPtr(qobject_cast((Connection*) object().data()));
}
/**
* Returns the handle type specified in the operation.
*
* \return The handle type, as specified in #HandleType.
*/
HandleType PendingHandles::handleType() const
{
return mPriv->handleType;
}
/**
* Returns whether the operation was a handle request (as opposed to a
* reference of existing handles).
*
* \sa isReference()
*
* \return Whether the operation was a request (== !isReference()).
*/
bool PendingHandles::isRequest() const
{
return mPriv->isRequest;
}
/**
* Returns whether the operation was a handle reference (as opposed to a
* request for new handles).
*
* \sa isRequest()
*
* \return Whether the operation was a reference (== !isRequest()).
*/
bool PendingHandles::isReference() const
{
return !mPriv->isRequest;
}
/**
* If the operation was a request (as returned by isRequest()), returns the
* names of the entities for which handles were requested for. Otherwise,
* returns an empty list.
*
* \return Reference to a list of the names of the entities.
*/
const QStringList &PendingHandles::namesRequested() const
{
return mPriv->namesRequested;
}
QStringList PendingHandles::validNames() const
{
if (!isFinished()) {
warning() << "PendingHandles::validNames called before finished";
return QStringList();
} else if (!isValid()) {
warning() << "PendingHandles::validNames called when not valid";
return QStringList();
}
return mPriv->validNames;
}
QHash > PendingHandles::invalidNames() const
{
if (!isFinished()) {
warning() << "PendingHandles::invalidNames called before finished";
return QHash >();
}
return mPriv->invalidNames;
}
/**
* If the operation was a reference (as returned by isReference()), returns
* the handles which were to be referenced. Otherwise, returns an empty
* list.
*
* \return Reference to a list of the handles specified to be referenced.
*/
const UIntList &PendingHandles::handlesToReference() const
{
return mPriv->handlesToReference;
}
/**
* Returns the now-referenced handles resulting from the operation. If the
* operation has not (yet) finished successfully (isFinished() returns
* false
), the return value is undefined.
*
* For requests of new handles, handles()[i]
will be the handle
* corresponding to the entity name namesToRequest()[i]
. For
* references of existing handles, handles()[i] ==
* handlesToReference()[i]
will be true for any i
.
*
* \return ReferencedHandles instance containing the handles.
*/
ReferencedHandles PendingHandles::handles() const
{
if (!isFinished()) {
warning() << "PendingHandles::handles() called before finished";
return ReferencedHandles();
} else if (!isValid()) {
warning() << "PendingHandles::handles() called when not valid";
return ReferencedHandles();
}
return mPriv->handles;
}
UIntList PendingHandles::invalidHandles() const
{
if (!isFinished()) {
warning() << "PendingHandles::invalidHandles called before finished";
}
return mPriv->invalidHandles;
}
void PendingHandles::onRequestHandlesFinished(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply reply = *watcher;
if (reply.isError()) {
QDBusError error = reply.error();
if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
// do not fallback
foreach (const QString &name, mPriv->namesRequested) {
mPriv->invalidNames.insert(name,
QPair(error.name(),
error.message()));
}
setFinishedWithError(error);
connection()->handleRequestLanded(mPriv->handleType);
watcher->deleteLater();
return;
}
if (mPriv->namesRequested.size() == 1) {
debug().nospace() << " Failure: error " <<
reply.error().name() << ": " <<
reply.error().message();
mPriv->invalidNames.insert(mPriv->namesRequested.first(),
QPair(error.name(),
error.message()));
setFinished();
connection()->handleRequestLanded(mPriv->handleType);
watcher->deleteLater();
return;
}
// try to request one handles at a time
foreach (const QString &name, mPriv->namesRequested) {
QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(
connection()->baseInterface()->RequestHandles(
mPriv->handleType,
QStringList() << name),
this);
connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher*)),
SLOT(onRequestHandlesFallbackFinished(QDBusPendingCallWatcher*)));
mPriv->idsForWatchers.insert(watcher, name);
}
} else {
debug() << "Received reply to RequestHandles";
mPriv->handles = ReferencedHandles(connection(),
mPriv->handleType, reply.value());
mPriv->validNames.append(mPriv->namesRequested);
setFinished();
connection()->handleRequestLanded(mPriv->handleType);
}
}
void PendingHandles::onHoldHandlesFinished(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply reply = *watcher;
debug() << "Received reply to HoldHandles";
if (reply.isError()) {
debug().nospace() << " Failure: error " <<
reply.error().name() << ": " <<
reply.error().message();
QDBusError error = reply.error();
if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
// do not fallback
mPriv->invalidHandles = mPriv->handlesToReference;
setFinishedWithError(error);
watcher->deleteLater();
return;
}
if (mPriv->handlesToReference.size() == 1) {
debug().nospace() << " Failure: error " <<
reply.error().name() << ": " <<
reply.error().message();
mPriv->invalidHandles = mPriv->handlesToReference;
setFinished();
watcher->deleteLater();
return;
}
// try to request one handles at a time
foreach (uint handle, mPriv->handlesToReference) {
QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(
connection()->baseInterface()->HoldHandles(
mPriv->handleType,
UIntList() << handle),
this);
connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher*)),
SLOT(onHoldHandlesFallbackFinished(QDBusPendingCallWatcher*)));
mPriv->handlesForWatchers.insert(watcher, handle);
}
} else {
mPriv->handles = ReferencedHandles(connection(),
mPriv->handleType, mPriv->handlesToReference);
setFinished();
}
watcher->deleteLater();
}
void PendingHandles::onRequestHandlesFallbackFinished(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply reply = *watcher;
Q_ASSERT(mPriv->idsForWatchers.contains(watcher));
QString id = mPriv->idsForWatchers.value(watcher);
debug() << "Received reply to RequestHandles(" << id << ")";
if (reply.isError()) {
debug().nospace() << " Failure: error " << reply.error().name() << ": "
<< reply.error().message();
// if the error is disconnected for example, fail immediately
QDBusError error = reply.error();
if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
foreach (const QString &name, mPriv->namesRequested) {
mPriv->invalidNames.insert(name,
QPair(error.name(),
error.message()));
}
setFinishedWithError(error);
connection()->handleRequestLanded(mPriv->handleType);
watcher->deleteLater();
return;
}
mPriv->invalidNames.insert(id,
QPair(reply.error().name(),
reply.error().message()));
} else {
Q_ASSERT(reply.value().size() == 1);
uint handle = reply.value().first();
mPriv->handlesForIds.insert(id, handle);
}
if (++mPriv->requestsFinished == mPriv->namesRequested.size()) {
if (mPriv->handlesForIds.size() == 0) {
// all requests failed
setFinished();
} else {
// all requests either failed or finished successfully
// we need to return the handles in the same order as requested
UIntList handles;
foreach (const QString &name, mPriv->namesRequested) {
if (!mPriv->invalidNames.contains(name)) {
Q_ASSERT(mPriv->handlesForIds.contains(name));
handles.append(mPriv->handlesForIds.value(name));
mPriv->validNames.append(name);
}
}
mPriv->handles = ReferencedHandles(connection(),
mPriv->handleType, handles);
setFinished();
}
debug() << " namesRequested:" << mPriv->namesRequested;
debug() << " invalidNames :" << mPriv->invalidNames;
debug() << " validNames :" << mPriv->validNames;
connection()->handleRequestLanded(mPriv->handleType);
}
watcher->deleteLater();
}
void PendingHandles::onHoldHandlesFallbackFinished(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply reply = *watcher;
Q_ASSERT(mPriv->handlesForWatchers.contains(watcher));
uint handle = mPriv->handlesForWatchers.value(watcher);
debug() << "Received reply to HoldHandles(" << handle << ")";
if (reply.isError()) {
debug().nospace() << " Failure: error " << reply.error().name() << ": "
<< reply.error().message();
// if the error is disconnected for example, fail immediately
QDBusError error = reply.error();
if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
mPriv->invalidHandles = mPriv->handlesToReference;
setFinishedWithError(error);
watcher->deleteLater();
return;
}
mPriv->invalidHandles.append(handle);
}
if (++mPriv->requestsFinished == mPriv->namesRequested.size()) {
// we need to return the handles in the same order as requested
UIntList handles;
foreach (uint handle, mPriv->handlesToReference) {
if (!mPriv->invalidHandles.contains(handle)) {
handles.append(handle);
}
}
if (handles.size() != 0) {
mPriv->handles = ReferencedHandles(connection(),
mPriv->handleType, handles);
}
setFinished();
}
watcher->deleteLater();
}
} // Tp