1 /*
2  * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
3  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4  */
5 
6 
7 
8 #ifndef DOMAIN_LIVEQUERY_H
9 #define DOMAIN_LIVEQUERY_H
10 
11 #include "queryresult.h"
12 
13 namespace Domain {
14 
15 template <typename InputType>
16 class LiveQueryInput
17 {
18 public:
19     typedef QSharedPointer<LiveQueryInput<InputType>> Ptr;
20     typedef QWeakPointer<LiveQueryInput<InputType>> WeakPtr;
21     typedef QList<Ptr> List;
22     typedef QList<WeakPtr> WeakList;
23 
24     typedef std::function<void(const InputType &)> AddFunction;
25     typedef std::function<void(const AddFunction &)> FetchFunction;
26     typedef std::function<bool(const InputType &)> PredicateFunction;
27 
~LiveQueryInput()28     virtual ~LiveQueryInput() {}
29 
30     virtual void reset() = 0;
31     virtual void onAdded(const InputType &input) = 0;
32     virtual void onChanged(const InputType &input) = 0;
33     virtual void onRemoved(const InputType &input) = 0;
34 };
35 
36 template <typename OutputType>
37 class LiveQueryOutput
38 {
39 public:
40     typedef QSharedPointer<LiveQueryOutput<OutputType>> Ptr;
41     typedef QList<Ptr> List;
42     typedef QueryResult<OutputType> Result;
43 
~LiveQueryOutput()44     virtual ~LiveQueryOutput() {}
45     virtual typename Result::Ptr result() = 0;
46     virtual void reset() = 0;
47 };
48 
49 template<typename InputType, typename OutputType>
50 class LiveQuery : public LiveQueryInput<InputType>, public LiveQueryOutput<OutputType>
51 {
52 public:
53     typedef QSharedPointer<LiveQuery<InputType, OutputType>> Ptr;
54     typedef QList<Ptr> List;
55 
56     typedef QueryResultProvider<OutputType> Provider;
57     typedef QueryResult<OutputType> Result;
58 
59     typedef typename LiveQueryInput<InputType>::AddFunction AddFunction;
60     typedef typename LiveQueryInput<InputType>::FetchFunction FetchFunction;
61     typedef typename LiveQueryInput<InputType>::PredicateFunction PredicateFunction;
62 
63     typedef std::function<OutputType(const InputType &)> ConvertFunction;
64     typedef std::function<void(const InputType &, OutputType &)> UpdateFunction;
65     typedef std::function<bool(const InputType &, const OutputType &)> RepresentsFunction;
66 
67     LiveQuery() = default;
68     LiveQuery(const LiveQuery &other) = default;
69     LiveQuery &operator=(const LiveQuery &other) = default;
70 
~LiveQuery()71     ~LiveQuery()
72     {
73         clear();
74     }
75 
result()76     typename Result::Ptr result() override
77     {
78         typename Provider::Ptr provider(m_provider.toStrongRef());
79 
80         if (provider)
81             return Result::create(provider);
82 
83         provider = Provider::Ptr::create();
84         m_provider = provider.toWeakRef();
85 
86         doFetch();
87 
88         return Result::create(provider);
89     }
90 
setFetchFunction(const FetchFunction & fetch)91     void setFetchFunction(const FetchFunction &fetch)
92     {
93         m_fetch = fetch;
94     }
95 
setPredicateFunction(const PredicateFunction & predicate)96     void setPredicateFunction(const PredicateFunction &predicate)
97     {
98         m_predicate = predicate;
99     }
100 
setConvertFunction(const ConvertFunction & convert)101     void setConvertFunction(const ConvertFunction &convert)
102     {
103         m_convert = convert;
104     }
105 
setUpdateFunction(const UpdateFunction & update)106     void setUpdateFunction(const UpdateFunction &update)
107     {
108         m_update = update;
109     }
110 
setDebugName(const QByteArray & name)111     void setDebugName(const QByteArray &name)
112     {
113         m_debugName = name;
114     }
115 
setRepresentsFunction(const RepresentsFunction & represents)116     void setRepresentsFunction(const RepresentsFunction &represents)
117     {
118         m_represents = represents;
119     }
120 
reset()121     void reset() override
122     {
123         clear();
124         doFetch();
125     }
126 
onAdded(const InputType & input)127     void onAdded(const InputType &input) override
128     {
129         typename Provider::Ptr provider(m_provider.toStrongRef());
130 
131         if (!provider)
132             return;
133 
134         if (m_predicate(input))
135             addToProvider(provider, input);
136     }
137 
onChanged(const InputType & input)138     void onChanged(const InputType &input) override
139     {
140         typename Provider::Ptr provider(m_provider.toStrongRef());
141 
142         if (!provider)
143             return;
144 
145         if (!m_predicate(input)) {
146             for (int i = 0; i < provider->data().size(); i++) {
147                 auto output = provider->data().at(i);
148                 if (m_represents(input, output)) {
149                     provider->removeAt(i);
150                     i--;
151                 }
152             }
153         } else {
154             bool found = false;
155 
156             for (int i = 0; i < provider->data().size(); i++) {
157                 auto output = provider->data().at(i);
158                 if (m_represents(input, output)) {
159                     m_update(input, output);
160                     provider->replace(i, output);
161 
162                     found = true;
163                 }
164             }
165 
166             if (!found)
167                 addToProvider(provider, input);
168         }
169     }
170 
onRemoved(const InputType & input)171     void onRemoved(const InputType &input) override
172     {
173         typename Provider::Ptr provider(m_provider.toStrongRef());
174 
175         if (!provider)
176             return;
177 
178         for (int i = 0; i < provider->data().size(); i++) {
179             auto output = provider->data().at(i);
180             if (m_represents(input, output)) {
181                 provider->removeAt(i);
182                 i--;
183             }
184         }
185     }
186 
187 private:
188     template<typename T>
isValidOutput(const T &)189     bool isValidOutput(const T &/*output*/)
190     {
191         return true;
192     }
193 
194     template<typename T>
isValidOutput(const QSharedPointer<T> & output)195     bool isValidOutput(const QSharedPointer<T> &output)
196     {
197         return !output.isNull();
198     }
199 
200     template<typename T>
isValidOutput(T * output)201     bool isValidOutput(T *output)
202     {
203         return output != nullptr;
204     }
205 
addToProvider(const typename Provider::Ptr & provider,const InputType & input)206     void addToProvider(const typename Provider::Ptr &provider, const InputType &input)
207     {
208         auto output = m_convert(input);
209         if (isValidOutput(output))
210             provider->append(output);
211     }
212 
doFetch()213     void doFetch()
214     {
215         typename Provider::Ptr provider(m_provider.toStrongRef());
216 
217         if (!provider)
218             return;
219 
220         auto addFunction = [this, provider] (const InputType &input) {
221             if (m_predicate(input))
222                 addToProvider(provider, input);
223         };
224 
225         m_fetch(addFunction);
226     }
227 
clear()228     void clear()
229     {
230         typename Provider::Ptr provider(m_provider.toStrongRef());
231 
232         if (!provider)
233             return;
234 
235         while (!provider->data().isEmpty())
236             provider->removeFirst();
237     }
238 
239     FetchFunction m_fetch;
240     PredicateFunction m_predicate;
241     ConvertFunction m_convert;
242     UpdateFunction m_update;
243     RepresentsFunction m_represents;
244     QByteArray m_debugName;
245 
246     typename Provider::WeakPtr m_provider;
247 };
248 
249 // A query that stores an intermediate list of results (from the fetch), to react on changes on any item in that list
250 // and then filters that list with the predicate for the final result
251 // When one of the intermediary items changes, a full fetch is done again.
252 template<typename InputType, typename OutputType>
253 class LiveRelationshipQuery : public LiveQueryInput<InputType>, public LiveQueryOutput<OutputType>
254 {
255 public:
256     typedef QSharedPointer<LiveRelationshipQuery<InputType, OutputType>> Ptr;
257     typedef QList<Ptr> List;
258 
259     typedef QueryResultProvider<OutputType> Provider;
260     typedef QueryResult<OutputType> Result;
261 
262     typedef typename LiveQueryInput<InputType>::AddFunction AddFunction;
263     typedef typename LiveQueryInput<InputType>::FetchFunction FetchFunction;
264     typedef typename LiveQueryInput<InputType>::PredicateFunction PredicateFunction;
265 
266     typedef std::function<OutputType(const InputType &)> ConvertFunction;
267     typedef std::function<bool(const InputType &, const OutputType &)> RepresentsFunction;
268     typedef std::function<bool(const InputType &, const InputType &)> CompareFunction;
269 
270     LiveRelationshipQuery() = default;
271     LiveRelationshipQuery(const LiveRelationshipQuery &other) = default;
272     LiveRelationshipQuery &operator=(const LiveRelationshipQuery &other) = default;
273 
~LiveRelationshipQuery()274     ~LiveRelationshipQuery()
275     {
276         clear();
277     }
278 
result()279     typename Result::Ptr result() override
280     {
281         typename Provider::Ptr provider(m_provider.toStrongRef());
282 
283         if (provider)
284             return Result::create(provider);
285         provider = Provider::Ptr::create();
286         m_provider = provider.toWeakRef();
287 
288         doFetch();
289 
290         return Result::create(provider);
291     }
292 
setFetchFunction(const FetchFunction & fetch)293     void setFetchFunction(const FetchFunction &fetch)
294     {
295         m_fetch = fetch;
296     }
297 
setPredicateFunction(const PredicateFunction & predicate)298     void setPredicateFunction(const PredicateFunction &predicate)
299     {
300         m_predicate = predicate;
301     }
302 
setCompareFunction(const CompareFunction & compare)303     void setCompareFunction(const CompareFunction &compare)
304     {
305         m_compare = compare;
306     }
307 
setConvertFunction(const ConvertFunction & convert)308     void setConvertFunction(const ConvertFunction &convert)
309     {
310         m_convert = convert;
311     }
312 
setDebugName(const QByteArray & name)313     void setDebugName(const QByteArray &name)
314     {
315         m_debugName = name;
316     }
317 
setRepresentsFunction(const RepresentsFunction & represents)318     void setRepresentsFunction(const RepresentsFunction &represents)
319     {
320         m_represents = represents;
321     }
322 
reset()323     void reset() override
324     {
325         clear();
326         doFetch();
327     }
328 
onAdded(const InputType & input)329     void onAdded(const InputType &input) override
330     {
331         typename Provider::Ptr provider(m_provider.toStrongRef());
332 
333         if (!provider)
334             return;
335 
336         m_intermediaryResults.append(input);
337         if (m_predicate(input))
338             addToProvider(provider, input);
339     }
340 
onChanged(const InputType & input)341     void onChanged(const InputType &input) override
342     {
343         Q_ASSERT(m_compare);
344         const bool found = std::any_of(m_intermediaryResults.constBegin(), m_intermediaryResults.constEnd(),
345                                        [&input, this](const InputType &existing) {
346                                            return m_compare(input, existing);
347                                        });
348         if (found)
349             reset();
350     }
351 
onRemoved(const InputType & input)352     void onRemoved(const InputType &input) override
353     {
354         onChanged(input);
355     }
356 
357 private:
358     template<typename T>
isValidOutput(const T &)359     bool isValidOutput(const T &/*output*/)
360     {
361         return true;
362     }
363 
364     template<typename T>
isValidOutput(const QSharedPointer<T> & output)365     bool isValidOutput(const QSharedPointer<T> &output)
366     {
367         return !output.isNull();
368     }
369 
370     template<typename T>
isValidOutput(T * output)371     bool isValidOutput(T *output)
372     {
373         return output != nullptr;
374     }
375 
addToProvider(const typename Provider::Ptr & provider,const InputType & input)376     void addToProvider(const typename Provider::Ptr &provider, const InputType &input)
377     {
378         auto output = m_convert(input);
379         if (isValidOutput(output))
380             provider->append(output);
381     }
382 
doFetch()383     void doFetch()
384     {
385         auto addFunction = [this] (const InputType &input) {
386             onAdded(input);
387         };
388         m_fetch(addFunction);
389     }
390 
clear()391     void clear()
392     {
393         m_intermediaryResults.clear();
394 
395         typename Provider::Ptr provider(m_provider.toStrongRef());
396 
397         if (!provider)
398             return;
399 
400         while (!provider->data().isEmpty())
401             provider->removeFirst();
402     }
403 
404     FetchFunction m_fetch;
405     PredicateFunction m_predicate;
406     ConvertFunction m_convert;
407     CompareFunction m_compare;
408     RepresentsFunction m_represents;
409     QByteArray m_debugName;
410 
411     typename Provider::WeakPtr m_provider;
412     QList<InputType> m_intermediaryResults;
413 };
414 
415 }
416 
417 #endif // DOMAIN_LIVEQUERY_H
418