| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. |
| 4 | ** Contact: http://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtContacts module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at http://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 2.1 or version 3 as published by the Free |
| 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
| 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
| 22 | ** following information to ensure the GNU Lesser General Public License |
| 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
| 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 25 | ** |
| 26 | ** As a special exception, The Qt Company gives you certain additional |
| 27 | ** rights. These rights are described in The Qt Company LGPL Exception |
| 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| 29 | ** |
| 30 | ** $QT_END_LICENSE$ |
| 31 | ** |
| 32 | ****************************************************************************/ |
| 33 | |
| 34 | #include "qcontactabstractrequest.h" |
| 35 | #include "qcontactabstractrequest_p.h" |
| 36 | |
| 37 | #ifndef QT_NO_DEBUG_STREAM |
| 38 | #include <QtCore/qdebug.h> |
| 39 | #endif |
| 40 | |
| 41 | #include "qcontactmanager_p.h" |
| 42 | #include "qcontactmanagerengine.h" |
| 43 | |
| 44 | QT_BEGIN_NAMESPACE_CONTACTS |
| 45 | /*! |
| 46 | \class QContactAbstractRequest |
| 47 | |
| 48 | \brief The QContactAbstractRequest class provides a mechanism for |
| 49 | asynchronous requests to be made of a manager if it supports them. |
| 50 | |
| 51 | \inmodule QtContacts |
| 52 | |
| 53 | \ingroup contacts-main |
| 54 | |
| 55 | It allows a client to asynchronously request some functionality of a |
| 56 | particular QContactManager. Instances of the class will emit signals |
| 57 | when the state of the request changes, or when more results become |
| 58 | available. |
| 59 | |
| 60 | Clients should not attempt to create instances of this class directly, |
| 61 | but should instead use the use-case-specific classes derived from this |
| 62 | class. |
| 63 | |
| 64 | All such request classes have a similar interface: clients set the |
| 65 | parameters of the asynchronous call, including which manager the |
| 66 | request will be made of, and then call the start() slot of the request. |
| 67 | The manager will then enqueue or begin to process the request, at which |
| 68 | point the request's state will transition from \c InactiveState to |
| 69 | \c ActiveState. After any state transition, the request will emit the |
| 70 | stateChanged() signal. The manager may periodically update the request |
| 71 | with results, at which point the request will emit the resultsAvailable() |
| 72 | signal. These results are not guaranteed to have a stable ordering. |
| 73 | Error information is considered a result, so some requests will emit the |
| 74 | resultsAvailable() signal even if no results are possible from the request |
| 75 | (for example, a contact remove request) when the manager updates the request |
| 76 | with information about any errors which may have occurred. |
| 77 | |
| 78 | Please see the class documentation of each of the use-case-specific |
| 79 | classes derived from this class for information about how to retrieve |
| 80 | the results of a request (including error information). In all cases, |
| 81 | those functions are synchronous, and will return the cached result set with |
| 82 | which the manager has updated the request instance if the resultsAvailable() |
| 83 | signal has been emitted. |
| 84 | |
| 85 | Clients can choose which signals they wish to handle from a request. |
| 86 | If the client is not interested in interim results, they can choose to |
| 87 | handle only the stateChanged() signal, and in the slot to which that |
| 88 | signal is connected, check whether the state has changed to either |
| 89 | \c FinishedState or \c CanceledState (both of which signify that the |
| 90 | manager has finished handling the request, and that the request will not |
| 91 | be updated with any more results). If the client is not interested in |
| 92 | any results (including error information), they may choose to delete |
| 93 | the request after calling \l start(), or simply not connect the |
| 94 | request's signals to any slots. |
| 95 | |
| 96 | If the request is allocated via operator new, the client must |
| 97 | delete the request when they are no longer using it in order to avoid |
| 98 | leaking memory. That is, the client retains ownership of the request. |
| 99 | |
| 100 | The client may delete a heap-allocated request in various ways: |
| 101 | by deleting it directly (but not within a slot connected to a signal |
| 102 | emitted by the request), or by using the deleteLater() slot to schedule |
| 103 | the request for deletion when control returns to the event loop (from |
| 104 | within a slot connected to a signal emitted by the request, for example |
| 105 | \l stateChanged()). |
| 106 | |
| 107 | An active request may be deleted by the client, but the client will not |
| 108 | receive any notifications about whether the request succeeded or not, |
| 109 | nor any results of the request. |
| 110 | |
| 111 | Because clients retain ownership of any request object, and may delete |
| 112 | a request object at any time, manager engine implementors must be careful |
| 113 | to ensure that they do not assume that a request has not been deleted |
| 114 | at some point during processing of a request, particularly if the engine |
| 115 | has a multithreaded implementation. It is suggested that engine |
| 116 | implementors read the \l{Qt Contacts Manager Engines} documentation for |
| 117 | more information on this topic. |
| 118 | */ |
| 119 | |
| 120 | /*! |
| 121 | \fn QContactAbstractRequest::stateChanged(QContactAbstractRequest::State newState) |
| 122 | This signal is emitted when the state of the request is changed. The new state of |
| 123 | the request will be contained in \a newState. |
| 124 | */ |
| 125 | |
| 126 | |
| 127 | /*! |
| 128 | \fn QContactAbstractRequest::resultsAvailable() |
| 129 | This signal is emitted when new results are available. Results can include |
| 130 | the operation error which may be accessed via error(), or derived-class-specific |
| 131 | results which are accessible through the derived class API. |
| 132 | |
| 133 | |
| 134 | \sa error() |
| 135 | */ |
| 136 | |
| 137 | /*! |
| 138 | \enum QContactAbstractRequest::RequestType |
| 139 | Enumerates the various possible types of asynchronous requests |
| 140 | \value InvalidRequest An invalid request |
| 141 | \value ContactFetchRequest A request to fetch a list of contacts |
| 142 | \value ContactIdFetchRequest A request to fetch a list of contact ids |
| 143 | \value ContactRemoveRequest A request to remove a list of contacts |
| 144 | \value ContactSaveRequest A request to save a list of contacts |
| 145 | \value RelationshipFetchRequest A request to fetch relationships between contacts |
| 146 | \value RelationshipRemoveRequest A request to remove any relationships which match the request criteria |
| 147 | \value RelationshipSaveRequest A request to save a list of relationships |
| 148 | \value ContactFetchByIdRequest A request to fetch a list of contacts given a list of ids |
| 149 | */ |
| 150 | |
| 151 | /*! |
| 152 | \enum QContactAbstractRequest::State |
| 153 | Enumerates the various states that a request may be in at any given time |
| 154 | \value InactiveState Operation not yet started |
| 155 | \value ActiveState Operation started, not yet finished |
| 156 | \value CanceledState Operation is finished due to cancellation |
| 157 | \value FinishedState Operation successfully completed |
| 158 | */ |
| 159 | |
| 160 | /*! |
| 161 | \fn QContactAbstractRequest::QContactAbstractRequest(QObject* parent) |
| 162 | Constructs a new, invalid asynchronous request with the specified \a parent |
| 163 | */ |
| 164 | |
| 165 | /*! |
| 166 | \internal |
| 167 | Constructs a new request from the given request data \a otherd with |
| 168 | the given parent \a parent |
| 169 | */ |
| 170 | QContactAbstractRequest::QContactAbstractRequest(QContactAbstractRequestPrivate* otherd, QObject* parent) |
| 171 | : QObject(parent), d_ptr(otherd) |
| 172 | { |
| 173 | } |
| 174 | |
| 175 | /*! Cleans up the memory used by this request */ |
| 176 | QContactAbstractRequest::~QContactAbstractRequest() |
| 177 | { |
| 178 | d_ptr->m_mutex.lock(); |
| 179 | QContactManagerEngine *engine = QContactManagerData::engine(manager: d_ptr->m_manager); |
| 180 | d_ptr->m_mutex.unlock(); |
| 181 | if (engine) |
| 182 | engine->requestDestroyed(req: this); |
| 183 | |
| 184 | delete d_ptr; |
| 185 | d_ptr = 0; |
| 186 | } |
| 187 | |
| 188 | /*! |
| 189 | \fn bool QContactAbstractRequest::isInactive() const |
| 190 | |
| 191 | Returns true if the request is in the \l QContactAbstractRequest::InactiveState state; |
| 192 | returns false otherwise. |
| 193 | |
| 194 | \sa state(), isActive(), isCanceled(), isFinished() |
| 195 | */ |
| 196 | |
| 197 | /*! |
| 198 | \fn bool QContactAbstractRequest::isActive() const |
| 199 | |
| 200 | Returns true if the request is in the \l QContactAbstractRequest::ActiveState state; |
| 201 | returns false otherwise. |
| 202 | |
| 203 | \sa state(), isInactive(), isCanceled(), isFinished() |
| 204 | */ |
| 205 | |
| 206 | /*! |
| 207 | \fn bool QContactAbstractRequest::isFinished() const |
| 208 | |
| 209 | Returns true if the request is in the \l QContactAbstractRequest::FinishedState; |
| 210 | returns false otherwise. |
| 211 | |
| 212 | \sa state(), isActive(), isInactive(), isCanceled() |
| 213 | */ |
| 214 | |
| 215 | /*! |
| 216 | \fn bool QContactAbstractRequest::isCanceled() const |
| 217 | |
| 218 | Returns true if the request is in the \l QContactAbstractRequest::CanceledState; |
| 219 | returns false otherwise. |
| 220 | |
| 221 | \sa state(), isActive(), isInactive(), isFinished() |
| 222 | */ |
| 223 | |
| 224 | /*! Returns the overall error of the most recent asynchronous operation |
| 225 | */ |
| 226 | QContactManager::Error QContactAbstractRequest::error() const |
| 227 | { |
| 228 | QMutexLocker ml(&d_ptr->m_mutex); |
| 229 | return d_ptr->m_error; |
| 230 | } |
| 231 | |
| 232 | /*! |
| 233 | Returns the type of this asynchronous request |
| 234 | */ |
| 235 | QContactAbstractRequest::RequestType QContactAbstractRequest::type() const |
| 236 | { |
| 237 | return d_ptr->m_type; |
| 238 | } |
| 239 | |
| 240 | /*! |
| 241 | Returns the current state of the request. |
| 242 | */ |
| 243 | QContactAbstractRequest::State QContactAbstractRequest::state() const |
| 244 | { |
| 245 | QMutexLocker ml(&d_ptr->m_mutex); |
| 246 | return d_ptr->m_state; |
| 247 | } |
| 248 | |
| 249 | /*! Returns a pointer to the manager of which this request instance requests operations |
| 250 | */ |
| 251 | QContactManager* QContactAbstractRequest::manager() const |
| 252 | { |
| 253 | QMutexLocker ml(&d_ptr->m_mutex); |
| 254 | return d_ptr->m_manager; |
| 255 | } |
| 256 | |
| 257 | /*! |
| 258 | Sets the manager of which this request instance requests operations to \a manager |
| 259 | |
| 260 | If the request is currently active, this function will return without updating the \a manager object. |
| 261 | */ |
| 262 | void QContactAbstractRequest::setManager(QContactManager* manager) |
| 263 | { |
| 264 | QMutexLocker ml(&d_ptr->m_mutex); |
| 265 | // In theory we might have been active and the manager didn't cancel/finish us |
| 266 | if (d_ptr->m_state == QContactAbstractRequest::ActiveState && d_ptr->m_manager) |
| 267 | return; |
| 268 | d_ptr->m_manager = manager; |
| 269 | } |
| 270 | |
| 271 | /*! Attempts to start the request. Returns false if the request is not in the \c QContactAbstractRequest::Inactive, \c QContactAbstractRequest::Finished or \c QContactAbstractRequest::Cancelled states, |
| 272 | or if the request was unable to be performed by the manager engine; otherwise returns true. |
| 273 | */ |
| 274 | bool QContactAbstractRequest::start() |
| 275 | { |
| 276 | QMutexLocker ml(&d_ptr->m_mutex); |
| 277 | QContactManagerEngine* engine = QContactManagerData::engine(manager: d_ptr->m_manager); |
| 278 | if (engine && (d_ptr->m_state == QContactAbstractRequest::CanceledState |
| 279 | || d_ptr->m_state == QContactAbstractRequest::FinishedState |
| 280 | || d_ptr->m_state == QContactAbstractRequest::InactiveState)) { |
| 281 | ml.unlock(); |
| 282 | return engine->startRequest(req: this); |
| 283 | } |
| 284 | |
| 285 | return false; // unable to start operation; another operation already in progress or no engine. |
| 286 | } |
| 287 | |
| 288 | /*! Attempts to cancel the request. Returns false if the request is not in the \c QContactAbstractRequest::Active state, |
| 289 | or if the request is unable to be cancelled by the manager engine; otherwise returns true. |
| 290 | */ |
| 291 | bool QContactAbstractRequest::cancel() |
| 292 | { |
| 293 | QMutexLocker ml(&d_ptr->m_mutex); |
| 294 | QContactManagerEngine* engine = QContactManagerData::engine(manager: d_ptr->m_manager); |
| 295 | if (engine && d_ptr->m_state == QContactAbstractRequest::ActiveState) { |
| 296 | ml.unlock(); |
| 297 | return engine->cancelRequest(req: this); |
| 298 | } |
| 299 | |
| 300 | return false; // unable to cancel operation; not in progress or no engine. |
| 301 | } |
| 302 | |
| 303 | /*! Blocks until the request has been completed by the manager engine, or until \a msecs milliseconds has elapsed. |
| 304 | If \a msecs is zero or negative, this function will block until the request is complete, regardless of how long it takes. |
| 305 | Returns true if the request was cancelled or completed successfully within the given period, otherwise false. |
| 306 | Some backends are unable to support this operation safely, and will return false immediately. |
| 307 | |
| 308 | Note that any signals generated while waiting for the request to complete may be queued and delivered |
| 309 | some time after this function has returned, when the calling thread's event loop is dispatched. If your code |
| 310 | depends on your slots being invoked, you may need to process events after calling this function. |
| 311 | */ |
| 312 | bool QContactAbstractRequest::waitForFinished(int msecs) |
| 313 | { |
| 314 | QMutexLocker ml(&d_ptr->m_mutex); |
| 315 | QContactManagerEngine* engine = QContactManagerData::engine(manager: d_ptr->m_manager); |
| 316 | if (engine) { |
| 317 | switch (d_ptr->m_state) { |
| 318 | case QContactAbstractRequest::ActiveState: |
| 319 | ml.unlock(); |
| 320 | return engine->waitForRequestFinished(req: this, msecs); |
| 321 | case QContactAbstractRequest::CanceledState: |
| 322 | case QContactAbstractRequest::FinishedState: |
| 323 | return true; |
| 324 | default: |
| 325 | return false; |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | return false; // unable to wait for operation; not in progress or no engine. |
| 330 | } |
| 331 | |
| 332 | #ifndef QT_NO_DEBUG_STREAM |
| 333 | /*! |
| 334 | Outputs \a request to the debug stream \a dbg |
| 335 | */ |
| 336 | QDebug operator<<(QDebug dbg, const QContactAbstractRequest& request) |
| 337 | { |
| 338 | dbg.nospace() << "QContactAbstractRequest(" ; |
| 339 | Q_ASSERT(request.d_ptr); |
| 340 | if (request.d_ptr->m_type != QContactAbstractRequest::InvalidRequest) { |
| 341 | QMutexLocker locker(&request.d_ptr->m_mutex); |
| 342 | request.d_ptr->debugStreamOut(dbg); |
| 343 | } else { |
| 344 | dbg.nospace() << "(null)" ; |
| 345 | } |
| 346 | dbg.nospace() << ")" ; |
| 347 | return dbg.maybeSpace(); |
| 348 | } |
| 349 | #endif |
| 350 | |
| 351 | #include "moc_qcontactabstractrequest.cpp" |
| 352 | |
| 353 | QT_END_NAMESPACE_CONTACTS |
| 354 | |