QtRestClient  3.0.0
A library for generic JSON-based REST-APIs, with a mechanism to map JSON to Qt objects
paging.h
1 #ifndef QTRESTCLIENT_PAGING_H
2 #define QTRESTCLIENT_PAGING_H
3 
4 #include "QtRestClient/paging_fwd.h"
5 #include "QtRestClient/genericrestreply.h"
6 #include "QtRestClient/restclass.h"
7 
8 #include <QtCore/qsharedpointer.h>
9 #include <QtCore/qpointer.h>
10 
11 // ------------- Generic Implementation -------------
12 
13 namespace QtRestClient {
14 
15 namespace __private {
16 
17 template <typename T>
18 class PagingData : public QSharedData
19 {
20 public:
21  PagingData() = default;
22  PagingData(const PagingData &other) = default;
23 
25  QList<T> data;
26  QPointer<RestClient> client;
27 };
28 
29 }
30 
31 template<typename T>
33  d{new __private::PagingData<T>{}}
34 {}
35 
36 template<typename T>
37 Paging<T>::Paging(const Paging<T> &other) = default;
38 
39 template<typename T>
40 Paging<T>::Paging(Paging<T> &&other) noexcept = default;
41 
42 template<typename T>
43 Paging<T> &Paging<T>::operator=(const Paging<T> &other) = default;
44 
45 template<typename T>
46 Paging<T> &Paging<T>::operator=(Paging<T> &&other) noexcept = default;
47 
48 template<typename T>
49 Paging<T>::Paging(IPaging *iPaging, const QList<T> &data, RestClient *client) :
50  d{new __private::PagingData<T>{}}
51 {
52  d->iPaging.reset(iPaging);
53  d->data = data;
54  d->client = client;
55 }
56 
57 template<typename T>
58 bool Paging<T>::isValid() const
59 {
60  return d->iPaging;
61 }
62 
63 template<typename T>
65 {
66  return d->iPaging.data();
67 }
68 
69 template<typename T>
71 {
72  return d->data;
73 }
74 
75 template<typename T>
76 qint64 Paging<T>::total() const
77 {
78  return d->iPaging->total();
79 }
80 
81 template<typename T>
82 qint64 Paging<T>::offset() const
83 {
84  return d->iPaging->offset();
85 }
86 
87 template<typename T>
88 bool Paging<T>::hasNext() const
89 {
90  return d->iPaging->hasNext();
91 }
92 
93 template<typename T>
94 template<typename EO>
96 {
97  if (d->iPaging->hasNext())
98  return d->client->rootClass()->template get<Paging<T>, EO>(d->iPaging->next());
99  else
100  return nullptr;
101 }
102 
103 template<typename T>
105 {
106  return d->iPaging->next();
107 }
108 
109 template<typename T>
111 {
112  return d->iPaging->hasPrevious();
113 }
114 
115 template<typename T>
116 template<typename EO>
118 {
119  if (d->iPaging->hasPrevious())
120  return d->client->rootClass()->template get<Paging<T>, EO>(d->iPaging->previous());
121  else
122  return nullptr;
123 }
124 
125 template<typename T>
127 {
128  return d->iPaging->previous();
129 }
130 
131 template<typename T>
132 void Paging<T>::iterate(const std::function<bool (T, qint64)> &iterator, qint64 to, qint64 from) const
133 {
134  Q_ASSERT(from >= d->iPaging->offset());
135 
136  auto index = internalIterate(iterator, to ,from);
137  if (index < 0)
138  return;
139 
140  //continue to the next one
141  auto max = calcMax(to);
142  if (index < max && d->iPaging->hasNext()) {
143  qCDebug(logPaging) << "Requesting next paging object with offset" << index
144  << "as" << d->iPaging->next().toString(QUrl::PrettyDecoded | QUrl::RemoveUserInfo);
145  next()->onSucceeded([iterator, to, index](int, const Paging<T> &paging) {
146  if (paging.isValid())
147  paging.iterate(iterator, to, index);
148  });
149  }
150 }
151 
152 template<typename T>
153 void Paging<T>::iterate(QObject *scope, const std::function<bool (T, qint64)> &iterator, qint64 to, qint64 from) const
154 {
155  Q_ASSERT(from >= d->iPaging->offset());
156 
157  auto index = internalIterate(iterator, to ,from);
158  if (index < 0)
159  return;
160 
161  //continue to the next one
162  auto max = calcMax(to);
163  if (index < max && d->iPaging->hasNext()) {
164  qCDebug(logPaging) << "Requesting next paging object with offset" << index
165  << "as" << d->iPaging->next().toString(QUrl::PrettyDecoded | QUrl::RemoveUserInfo);
166  next()->onSucceeded(scope, [scope, iterator, to, index](int, const Paging<T> &paging) {
167  if (paging.isValid())
168  paging.iterate(scope, iterator, to, index);
169  });
170  }
171 }
172 
173 template<typename T>
174 template<typename EO>
175 void Paging<T>::iterate(const std::function<bool(T, qint64)> &iterator, const std::function<void(QString, int, RestReply::Error)> &errorHandler, const std::function<QString (EO, int)> &failureTransformer, qint64 to, qint64 from) const
176 {
177  Q_ASSERT(from >= d->iPaging->offset());
178 
179  auto index = internalIterate(iterator, to ,from);
180  if (index < 0)
181  return;
182 
183  //continue to the next one
184  auto max = calcMax(to);
185  if (index < max && d->iPaging->hasNext()) {
186  qCDebug(logPaging) << "Requesting next paging object with offset" << index
187  << "as" << d->iPaging->next().toString(QUrl::PrettyDecoded | QUrl::RemoveUserInfo);
188  next<EO>()->onSucceeded([iterator, errorHandler, failureTransformer, to, index](int, const Paging<T> &paging) {
189  if (paging.isValid())
190  paging.iterate(iterator, errorHandler, failureTransformer, to, index);
191  })
192  ->onAllErrors(errorHandler, failureTransformer);
193  }
194 }
195 
196 template<typename T>
197 template<typename EO>
198 void Paging<T>::iterate(QObject *scope, const std::function<bool(T, qint64)> &iterator, const std::function<void(QString, int, RestReply::Error)> &errorHandler, const std::function<QString (EO, int)> &failureTransformer, qint64 to, qint64 from) const
199 {
200  Q_ASSERT(from >= d->iPaging->offset());
201 
202  auto index = internalIterate(iterator, to ,from);
203  if (index < 0)
204  return;
205 
206  //continue to the next one
207  auto max = calcMax(to);
208  if (index < max && d->iPaging->hasNext()) {
209  qCDebug(logPaging) << "Requesting next paging object with offset" << index
210  << "as" << d->iPaging->next().toString(QUrl::PrettyDecoded | QUrl::RemoveUserInfo);
211  next<EO>()->onSucceeded(scope, [scope, iterator, errorHandler, failureTransformer, to, index](int, const Paging<T> &paging) {
212  if (paging.isValid())
213  paging.iterate(scope, iterator, errorHandler, failureTransformer, to, index);
214  })
215  ->onAllErrors(scope, errorHandler, failureTransformer);
216  }
217 }
218 
219 template<typename T>
220 template<typename EO>
221 void Paging<T>::iterate(const std::function<bool(T, qint64)> &iterator, const std::function<void(int, EO)> &failureHandler, const std::function<void(QString, int, RestReply::Error)> &errorHandler, const std::function<void(QtJsonSerializer::Exception &)> &exceptionHandler, qint64 to, qint64 from) const
222 {
223  Q_ASSERT(from >= d->iPaging->offset());
224 
225  auto index = internalIterate(iterator, to ,from);
226  if (index < 0)
227  return;
228 
229  //continue to the next one
230  auto max = calcMax(to);
231  if (index < max && d->iPaging->hasNext()) {
232  qCDebug(logPaging) << "Requesting next paging object with offset" << index
233  << "as" << d->iPaging->next().toString(QUrl::PrettyDecoded | QUrl::RemoveUserInfo);
234  next<EO>()->onSucceeded([iterator, failureHandler, errorHandler, exceptionHandler, to, index](int, const Paging<T> &paging) {
235  if (paging.isValid())
236  paging.iterate(iterator, failureHandler, errorHandler, exceptionHandler, to, index);
237  })
238  ->onFailed(failureHandler)
239  ->onError(errorHandler)
240  ->onSerializeException(exceptionHandler);
241  }
242 }
243 
244 template<typename T>
245 template<typename EO>
246 void Paging<T>::iterate(QObject *scope, const std::function<bool(T, qint64)> &iterator, const std::function<void(int, EO)> &failureHandler, const std::function<void(QString, int, RestReply::Error)> &errorHandler, const std::function<void(QtJsonSerializer::Exception &)> &exceptionHandler, qint64 to, qint64 from) const
247 {
248  Q_ASSERT(from >= d->iPaging->offset());
249 
250  auto index = internalIterate(iterator, to ,from);
251  if (index < 0)
252  return;
253 
254  //continue to the next one
255  auto max = calcMax(to);
256  if(index < max && d->iPaging->hasNext()) {
257  qCDebug(logPaging) << "Requesting next paging object with offset" << index
258  << "as" << d->iPaging->next().toString(QUrl::PrettyDecoded | QUrl::RemoveUserInfo);
259  next<EO>()->onSucceeded(scope, [scope, iterator, failureHandler, errorHandler, exceptionHandler, to, index](int, const Paging<T> &paging) {
260  if (paging.isValid())
261  paging.iterate(scope, iterator, failureHandler, errorHandler, exceptionHandler, to, index);
262  })
263  ->onFailed(scope, failureHandler)
264  ->onError(scope, errorHandler)
265  ->onSerializeException(exceptionHandler);
266  }
267 }
268 
269 template<typename T>
270 QVariantMap Paging<T>::properties() const
271 {
272  return d->iPaging->properties();
273 }
274 
275 template<typename T>
277 {
278  __private::MetaComponent<T>::deleteAllLater(d->data);
279 }
280 
281 template<typename T>
282 qint64 Paging<T>::internalIterate(const std::function<bool (T, qint64)> &iterator, qint64 to, qint64 from) const
283 {
284  // handle all items in this paging
285  auto offset = d->iPaging->offset();
286  auto count = d->data.size();
287  int start = 0;
288  int max = count;
289  if (offset >= 0) { // has indexes
290  start = static_cast<int>(std::max(from, offset) - offset);
291  if (to >= 0)
292  max = static_cast<int>(std::min(to, offset + count) - offset);
293  }
294 
295  qCDebug(logPaging).nospace() << "iterating over available range ["
296  << offset + start << ":"
297  << offset + max - 1 << "]";
298 
299  // delete unused items caused by from
300  for (auto j = 0; j < start; ++j)
301  __private::MetaComponent<T>::deleteLater(d->data.value(j));
302 
303  // iterate over used items
304  int i;
305  auto canceled = false;
306  for (i = start; i < max; ++i) {
307  auto item = d->data.value(i);
308  auto index = offset >= 0 ? offset + i : -1ll;
309  if (!iterator(item, index)) {
310  qCDebug(logPaging) << "Iterator stopped paging iteration at index"
311  << index;
312  canceled = true;
313  break;
314  }
315  }
316 
317  //delete all unused items caused by to
318  for (auto j = i; j < count; ++j)
319  __private::MetaComponent<T>::deleteLater(d->data.value(j));
320 
321  if (canceled)
322  return -1;
323  else if (offset >= 0)
324  return offset + i;
325  else
326  return 0;
327 }
328 
329 template<typename T>
330 qint64 Paging<T>::calcMax(qint64 to) const
331 {
332  if (d->iPaging->offset() >= 0) {
333  if (to >= 0)
334  return std::min(to, d->iPaging->total());
335  else
336  return d->iPaging->total();
337  } else
338  return std::numeric_limits<qint64>::max();
339 }
340 
341 }
342 
343 #endif // QTRESTCLIENT_PAGING_H
QPointer
QSharedData
QtRestClient::IPaging
Interface to parse generic paging objects and operate on them.
Definition: ipaging.h:22
QtRestClient::Paging::previous
GenericRestReply< Paging< T >, EO > * previous() const
Performs a request for the previous paging object.
Definition: paging.h:117
QtRestClient
The Namespace containing all classes of the QtRestClient module.
Definition: genericrestreply.h:14
QUrl
QtRestClient::RestReply::Error
Error
Defines the different possible error types.
Definition: restreply.h:45
QtRestClient::Paging::iterate
void iterate(const std::function< bool(T, qint64)> &iterator, qint64 to=-1, qint64 from=0) const
Iterates over all paging objects.
Definition: paging.h:132
QtRestClient::Paging::isValid
bool isValid() const
Returns true, if the current paging object is a valid one.
Definition: paging.h:58
QtRestClient::Paging::hasPrevious
bool hasPrevious() const
Returns true, if there is a previous paging object.
Definition: paging.h:110
QSharedPointer< IPaging >
QtRestClient::Paging::nextUrl
QUrl nextUrl() const
Returns the link to the next paging object.
Definition: paging.h:104
QtRestClient::Paging::total
qint64 total() const
Returns the total number of objects there are.
Definition: paging.h:76
QList
QtRestClient::Paging::hasNext
bool hasNext() const
Returns true, if there is a next paging object.
Definition: paging.h:88
QString
QtRestClient::Paging::Paging
Paging()
Default Constructor.
Definition: paging.h:32
QtRestClient::Paging::previousUrl
QUrl previousUrl() const
Returns the link to the previous paging object.
Definition: paging.h:126
QtRestClient::GenericRestReply
A class to handle generic replies for generic requests.
Definition: genericrestreply.h:20
QtRestClient::Paging::items
QList< T > items() const
Returns the items of this paging object, i.e. it's data.
Definition: paging.h:70
QtRestClient::Paging::iPaging
IPaging * iPaging() const
Returns the internally used IPaging instance.
Definition: paging.h:64
QtRestClient::IPaging::offset
virtual qint64 offset() const
Returns the offset this paging begins at.
QtRestClient::Paging
A class to access generic paging objects.
Definition: paging_fwd.h:30
QtRestClient::Paging::deleteAllItems
void deleteAllItems() const
Deletes all items this paging object is holding (QObjects only)
Definition: paging.h:276
QtRestClient::Paging::next
GenericRestReply< Paging< T >, EO > * next() const
Performs a request for the next paging object.
Definition: paging.h:95
QtJsonSerializer::Exception
QtRestClient::Paging::offset
qint64 offset() const
Returns the offset this paging begins at.
Definition: paging.h:82
QtRestClient::Paging::properties
QVariantMap properties() const
Returns a hash containing all properties of the original JSON.
Definition: paging.h:270
QObject
QtRestClient::RestClient
A class to define access to an API, with general settings.
Definition: restclient.h:30
QtRestClient::Paging::operator=
Paging< T > & operator=(const Paging< T > &other)
Copy assignment operator.