1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #ifndef QPLACEMANAGERENGINE_TEST_H
30 #define QPLACEMANAGERENGINE_TEST_H
31 
32 #include <QtCore/QDateTime>
33 #include <QtCore/QFile>
34 #include <QtCore/QJsonDocument>
35 #include <QtCore/QJsonObject>
36 #include <QtCore/QJsonArray>
37 #include <QtCore/QUuid>
38 #include <QtPositioning/QGeoCoordinate>
39 #include <QtPositioning/QGeoLocation>
40 #include <QtLocation/QPlaceContentReply>
41 #include <QtLocation/QPlaceManager>
42 #include <QtLocation/QPlaceManagerEngine>
43 #include <QtLocation/QPlaceReply>
44 #include <QtLocation/QPlaceDetailsReply>
45 #include <QtLocation/QPlaceEditorial>
46 #include <QtLocation/QPlaceIdReply>
47 #include <QtLocation/QPlaceImage>
48 #include <QtLocation/QPlaceSearchSuggestionReply>
49 #include <QtLocation/QPlaceSearchReply>
50 #include <QtLocation/QPlaceResult>
51 #include <QtLocation/QPlaceCategory>
52 #include <QtLocation/QPlace>
53 #include <QtLocation/QPlaceReview>
54 #include <QtLocation/private/qplace_p.h>
55 #include <QtTest/QTest>
56 
57 QT_BEGIN_NAMESPACE
58 
qHash(const QPlaceCategory & category)59 inline uint qHash(const QPlaceCategory &category)
60 {
61     return qHash(QUuid(category.categoryId().toLatin1()));
62 }
63 
64 QT_END_NAMESPACE
65 
66 QT_USE_NAMESPACE
67 
68 class QPlacePrivateDefaultAlt : public QPlacePrivateDefault
69 {
70 public:
QPlacePrivateDefaultAlt()71     QPlacePrivateDefaultAlt() {}
QPlacePrivateDefaultAlt(const QPlacePrivateDefaultAlt & other)72     QPlacePrivateDefaultAlt(const QPlacePrivateDefaultAlt &other)
73         : QPlacePrivateDefault(other)
74     {
75     }
~QPlacePrivateDefaultAlt()76     ~QPlacePrivateDefaultAlt() {}
77 
extendedAttribute(const QString & attributeType)78     QPlaceAttribute extendedAttribute(const QString &attributeType) const override
79     {
80         if (attributeType == QStringLiteral("x_provider")) {
81             QPlaceAttribute a;
82             a.setLabel(QStringLiteral("x_provider"));
83             a.setText(QStringLiteral("QPlacePrivateDefaultAlt"));
84             return a;
85         } else {
86             return QPlacePrivateDefault::extendedAttribute(attributeType);
87         }
88     }
89 };
90 
91 class QPlaceAlt : public QPlace
92 {
93 public:
QPlaceAlt()94     QPlaceAlt() : QPlace(QSharedDataPointer<QPlacePrivate>(new QPlacePrivateDefaultAlt()))
95     {
96     }
97 };
98 
99 class PlaceReply : public QPlaceReply
100 {
101     Q_OBJECT
102 
103     friend class QPlaceManagerEngineTest;
104 
105 public:
106     PlaceReply(QObject *parent = 0)
QPlaceReply(parent)107     :   QPlaceReply(parent)
108     { }
109 
emitFinished()110     Q_INVOKABLE void emitFinished()
111     {
112         emit finished();
113     }
114 };
115 
116 class ContentReply : public QPlaceContentReply
117 {
118     Q_OBJECT
119 
120     friend class QPlaceManagerEngineTest;
121 
122 public:
123     ContentReply(QObject *parent = 0)
QPlaceContentReply(parent)124     : QPlaceContentReply(parent)
125     {}
126 
emitError()127     Q_INVOKABLE void emitError()
128     {
129         emit error(error(), errorString());
130     }
131 
emitFinished()132     Q_INVOKABLE void emitFinished()
133     {
134         emit finished();
135     }
136 };
137 
138 class DetailsReply : public QPlaceDetailsReply
139 {
140     Q_OBJECT
141 
142     friend class QPlaceManagerEngineTest;
143 
144 public:
145     DetailsReply(QObject *parent = 0)
QPlaceDetailsReply(parent)146     :   QPlaceDetailsReply(parent)
147     { }
148 
emitError()149     Q_INVOKABLE void emitError()
150     {
151         emit error(error(), errorString());
152     }
153 
emitFinished()154     Q_INVOKABLE void emitFinished()
155     {
156         emit finished();
157     }
158 };
159 
160 class IdReply : public QPlaceIdReply
161 {
162     Q_OBJECT
163 
164     friend class QPlaceManagerEngineTest;
165 
166 public:
167     IdReply(QPlaceIdReply::OperationType type, QObject *parent = 0)
QPlaceIdReply(type,parent)168     :   QPlaceIdReply(type, parent)
169     { }
170 
emitError()171     Q_INVOKABLE void emitError()
172     {
173         emit error(error(), errorString());
174     }
175 
emitFinished()176     Q_INVOKABLE void emitFinished()
177     {
178         emit finished();
179     }
180 };
181 
182 class PlaceSearchReply : public QPlaceSearchReply
183 {
184     Q_OBJECT
185 
186 public:
187     PlaceSearchReply(const QList<QPlaceSearchResult> &results, QObject *parent = 0)
QPlaceSearchReply(parent)188     :   QPlaceSearchReply(parent)
189     {
190         setResults(results);
191     }
192 
emitError()193     Q_INVOKABLE void emitError()
194     {
195         emit error(error(), errorString());
196     }
197 
emitFinished()198     Q_INVOKABLE void emitFinished()
199     {
200         emit finished();
201     }
202 };
203 
204 class SuggestionReply : public QPlaceSearchSuggestionReply
205 {
206     Q_OBJECT
207 
208 public:
209     SuggestionReply(const QStringList &suggestions, QObject *parent = 0)
QPlaceSearchSuggestionReply(parent)210     :   QPlaceSearchSuggestionReply(parent)
211     {
212         setSuggestions(suggestions);
213     }
214 
emitError()215     Q_INVOKABLE void emitError()
216     {
217         emit error(error(), errorString());
218     }
219 
emitFinished()220     Q_INVOKABLE void emitFinished()
221     {
222         emit finished();
223     }
224 };
225 
226 class QPlaceManagerEngineTest : public QPlaceManagerEngine
227 {
228     Q_OBJECT
229 public:
QPlaceManagerEngineTest(const QVariantMap & parameters)230     QPlaceManagerEngineTest(const QVariantMap &parameters)
231         : QPlaceManagerEngine(parameters)
232     {
233         m_locales << QLocale();
234         if (parameters.value(QStringLiteral("initializePlaceData"), false).toBool()) {
235             QFile placeData(QFINDTESTDATA("place_data.json"));
236             QVERIFY(placeData.exists());
237             if (placeData.open(QIODevice::ReadOnly)) {
238                 QJsonDocument document = QJsonDocument::fromJson(placeData.readAll());
239 
240                 if (document.isObject()) {
241                     QJsonObject o = document.object();
242 
243                     if (o.contains(QStringLiteral("categories"))) {
244                         QJsonArray categories = o.value(QStringLiteral("categories")).toArray();
245 
246                         for (int i = 0; i < categories.count(); ++i) {
247                             QJsonObject c = categories.at(i).toObject();
248 
249                             QPlaceCategory category;
250 
251                             category.setName(c.value(QStringLiteral("name")).toString());
252                             category.setCategoryId(c.value(QStringLiteral("id")).toString());
253 
254                             m_categories.insert(category.categoryId(), category);
255 
256                             const QString parentId = c.value(QStringLiteral("parentId")).toString();
257                             m_childCategories[parentId].append(category.categoryId());
258                         }
259                     }
260 
261                     if (o.contains(QStringLiteral("places"))) {
262                         QJsonArray places = o.value(QStringLiteral("places")).toArray();
263 
264                         for (int i = 0; i < places.count(); ++i) {
265                             QJsonObject p = places.at(i).toObject();
266 
267                             QPlace place;
268                             if (p.value(QStringLiteral("alternateImplementation")).toBool(false)) {
269                                 place = QPlaceAlt();
270                                 QPlaceAttribute att;
271                                 att.setLabel(QStringLiteral("x_provider"));
272                                 att.setText(QStringLiteral("42")); // Doesn't matter, wont be used.
273                                 place.setExtendedAttribute(QStringLiteral("x_provider"), att);
274                             }
275 
276                             place.setName(p.value(QStringLiteral("name")).toString());
277                             place.setPlaceId(p.value(QStringLiteral("id")).toString());
278 
279                             QList<QPlaceCategory> categories;
280                             QJsonArray ca = p.value(QStringLiteral("categories")).toArray();
281                             for (int j = 0; j < ca.count(); ++j) {
282                                 QPlaceCategory c = m_categories.value(ca.at(j).toString());
283                                 if (!c.isEmpty())
284                                     categories.append(c);
285                             }
286                             place.setCategories(categories);
287 
288                             QGeoCoordinate coordinate;
289                             QJsonObject lo = p.value(QStringLiteral("location")).toObject();
290                             coordinate.setLatitude(lo.value(QStringLiteral("latitude")).toDouble());
291                             coordinate.setLongitude(lo.value(QStringLiteral("longitude")).toDouble());
292 
293                             QGeoLocation location;
294                             location.setCoordinate(coordinate);
295 
296                             place.setLocation(location);
297 
298                             m_places.insert(place.placeId(), place);
299 
300                             QStringList recommendations;
301                             QJsonArray ra = p.value(QStringLiteral("recommendations")).toArray();
302                             for (int j = 0; j < ra.count(); ++j)
303                                 recommendations.append(ra.at(j).toString());
304                             m_placeRecommendations.insert(place.placeId(), recommendations);
305 
306                             QJsonArray revArray = p.value(QStringLiteral("reviews")).toArray();
307                             QList<QPlaceReview> reviews;
308                             for (int j = 0; j < revArray.count(); ++j) {
309                                 QJsonObject ro = revArray.at(j).toObject();
310                                 QPlaceReview review;
311                                 if (ro.contains(QStringLiteral("title")))
312                                     review.setTitle(ro.value(QStringLiteral("title")).toString());
313                                 if (ro.contains(QStringLiteral("text")))
314                                     review.setText(ro.value(QStringLiteral("text")).toString());
315 
316                                 if (ro.contains(QStringLiteral("language")))
317                                     review.setLanguage(ro.value("language").toString());
318 
319                                 if (ro.contains(QStringLiteral("rating")))
320                                     review.setRating(ro.value("rating").toDouble());
321 
322                                 if (ro.contains(QStringLiteral("dateTime")))
323                                     review.setDateTime(QDateTime::fromString(
324                                                            ro.value(QStringLiteral("dateTime")).toString(),
325                                                            QStringLiteral("hh:mm dd-MM-yyyy")));
326                                 if (ro.contains(QStringLiteral("reviewId")))
327                                     review.setReviewId(ro.value("reviewId").toString());
328 
329                                 reviews << review;
330                             }
331                             m_placeReviews.insert(place.placeId(), reviews);
332 
333                             QJsonArray imgArray = p.value(QStringLiteral("images")).toArray();
334                             QList<QPlaceImage> images;
335                             for (int j = 0; j < imgArray.count(); ++j) {
336                                 QJsonObject imgo = imgArray.at(j).toObject();
337                                 QPlaceImage image;
338                                 if (imgo.contains(QStringLiteral("url")))
339                                     image.setUrl(imgo.value(QStringLiteral("url")).toString());
340 
341                                 if (imgo.contains("imageId"))
342                                     image.setImageId(imgo.value(QStringLiteral("imageId")).toString());
343 
344                                 if (imgo.contains("mimeType"))
345                                     image.setMimeType(imgo.value(QStringLiteral("mimeType")).toString());
346 
347                                 images << image;
348                             }
349 
350                             m_placeImages.insert(place.placeId(), images);
351 
352                             QJsonArray edArray = p.value(QStringLiteral("editorials")).toArray();
353                             QList<QPlaceEditorial> editorials;
354                             for (int j = 0; j < edArray.count(); ++j) {
355                                 QJsonObject edo = edArray.at(j).toObject();
356                                 QPlaceEditorial editorial;
357                                 if (edo.contains(QStringLiteral("title")))
358                                     editorial.setTitle(edo.value(QStringLiteral("title")).toString());
359 
360                                 if (edo.contains(QStringLiteral("text")))
361                                     editorial.setText(edo.value(QStringLiteral("text")).toString());
362 
363                                 if (edo.contains(QStringLiteral("language")))
364                                     editorial.setLanguage(edo.value(QStringLiteral("language")).toString());
365 
366                                 editorials << editorial;
367                             }
368 
369                             m_placeEditorials.insert(place.placeId(), editorials);
370                         }
371                     }
372                 }
373             }
374         }
375     }
376 
getPlaceDetails(const QString & placeId)377     QPlaceDetailsReply *getPlaceDetails(const QString &placeId) override
378     {
379         DetailsReply *reply = new DetailsReply(this);
380 
381         if (placeId.isEmpty() || !m_places.contains(placeId)) {
382             reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist"));
383             QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection);
384         } else {
385             reply->setPlace(m_places.value(placeId));
386         }
387 
388         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
389 
390         return reply;
391     }
392 
getPlaceContent(const QPlaceContentRequest & query)393     QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &query) override
394     {
395         ContentReply *reply = new ContentReply(this);
396         if (query.placeId().isEmpty() || !m_places.contains(query.placeId())) {
397             reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist"));
398             QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection);
399 
400         } else {
401                 QPlaceContent::Collection collection;
402                 int totalCount = 0;
403                 switch (query.contentType()) {
404                 case QPlaceContent::ReviewType:
405                     totalCount = m_placeReviews.value(query.placeId()).count();
406                     break;
407                 case QPlaceContent::ImageType:
408                     totalCount = m_placeImages.value(query.placeId()).count();
409                     break;
410                 case QPlaceContent::EditorialType:
411                     totalCount = m_placeEditorials.value(query.placeId()).count();
412                 default:
413                     //do nothing
414                     break;
415                 }
416 
417                 QVariantMap context = query.contentContext().toMap();
418 
419                 int offset = context.value(QStringLiteral("offset"), 0).toInt();
420                 int max = (query.limit() == -1) ? totalCount
421                                                 : qMin(offset + query.limit(), totalCount);
422                 for (int i = offset; i < max; ++i) {
423                     switch (query.contentType()) {
424                     case QPlaceContent::ReviewType:
425                         collection.insert(i, m_placeReviews.value(query.placeId()).at(i));
426                         break;
427                     case QPlaceContent::ImageType:
428                         collection.insert(i, m_placeImages.value(query.placeId()).at(i));
429                         break;
430                     case QPlaceContent::EditorialType:
431                         collection.insert(i, m_placeEditorials.value(query.placeId()).at(i));
432                     default:
433                         //do nothing
434                         break;
435                     }
436                 }
437 
438                 reply->setContent(collection);
439                 reply->setTotalCount(totalCount);
440 
441                 if (max != totalCount) {
442                     context.clear();
443                     context.insert(QStringLiteral("offset"), offset + query.limit());
444                     QPlaceContentRequest request = query;
445                     request.setContentContext(context);
446                     reply->setNextPageRequest(request);
447                 }
448                 if (offset > 0) {
449                     context.clear();
450                     context.insert(QStringLiteral("offset"), qMin(0, offset - query.limit()));
451                     QPlaceContentRequest request = query;
452                     request.setContentContext(context);
453                     reply->setPreviousPageRequest(request);
454                 }
455         }
456 
457         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
458         return reply;
459     }
460 
search(const QPlaceSearchRequest & query)461     QPlaceSearchReply *search(const QPlaceSearchRequest &query) override
462     {
463         QList<QPlaceSearchResult> results;
464 
465         if (!query.searchTerm().isEmpty()) {
466             foreach (const QPlace &place, m_places) {
467                 if (!place.name().contains(query.searchTerm(), Qt::CaseInsensitive))
468                     continue;
469 
470                 QPlaceResult r;
471                 r.setPlace(place);
472                 r.setTitle(place.name());
473 
474                 results.append(r);
475             }
476         } else if (!query.categories().isEmpty()) {
477             const auto &categoryList = query.categories();
478             const QSet<QPlaceCategory> categories(categoryList.cbegin(), categoryList.cend());
479             for (const QPlace &place : qAsConst(m_places)) {
480                 const auto &placeCategoryList = place.categories();
481                 const QSet<QPlaceCategory> placeCategories(placeCategoryList.cbegin(), placeCategoryList.cend());
482                 if (!placeCategories.intersects(categories))
483                     continue;
484 
485                 QPlaceResult r;
486                 r.setPlace(place);
487                 r.setTitle(place.name());
488 
489                 results.append(r);
490             }
491         } else if (!query.recommendationId().isEmpty()) {
492             QStringList recommendations = m_placeRecommendations.value(query.recommendationId());
493             foreach (const QString &id, recommendations) {
494                 QPlaceResult r;
495                 r.setPlace(m_places.value(id));
496                 r.setTitle(r.place().name());
497 
498                 results.append(r);
499             }
500         }
501 
502         PlaceSearchReply *reply = new PlaceSearchReply(results, this);
503 
504         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
505 
506         return reply;
507     }
508 
searchSuggestions(const QPlaceSearchRequest & query)509     QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &query) override
510     {
511         QStringList suggestions;
512         if (query.searchTerm() == QLatin1String("test")) {
513             suggestions << QStringLiteral("test1");
514             suggestions << QStringLiteral("test2");
515             suggestions << QStringLiteral("test3");
516         }
517 
518         SuggestionReply *reply = new SuggestionReply(suggestions, this);
519 
520         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
521 
522         return reply;
523     }
524 
savePlace(const QPlace & place)525     QPlaceIdReply *savePlace(const QPlace &place) override
526     {
527         IdReply *reply = new IdReply(QPlaceIdReply::SavePlace, this);
528 
529         if (!place.placeId().isEmpty() && !m_places.contains(place.placeId())) {
530             reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist"));
531             QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection);
532         } else if (!place.placeId().isEmpty()) {
533             m_places.insert(place.placeId(), place);
534             reply->setId(place.placeId());
535         } else {
536             QPlace p = place;
537             p.setPlaceId(QUuid::createUuid().toString());
538             m_places.insert(p.placeId(), p);
539 
540             reply->setId(p.placeId());
541         }
542 
543         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
544 
545         return reply;
546     }
547 
removePlace(const QString & placeId)548     QPlaceIdReply *removePlace(const QString &placeId) override
549     {
550         IdReply *reply = new IdReply(QPlaceIdReply::RemovePlace, this);
551         reply->setId(placeId);
552 
553         if (!m_places.contains(placeId)) {
554             reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist"));
555             QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection);
556         } else {
557             m_places.remove(placeId);
558         }
559 
560         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
561 
562         return reply;
563     }
564 
saveCategory(const QPlaceCategory & category,const QString & parentId)565     QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId) override
566     {
567         IdReply *reply = new IdReply(QPlaceIdReply::SaveCategory, this);
568 
569         if ((!category.categoryId().isEmpty() && !m_categories.contains(category.categoryId())) ||
570             (!parentId.isEmpty() && !m_categories.contains(parentId))) {
571             reply->setError(QPlaceReply::CategoryDoesNotExistError, tr("Category does not exist"));
572             QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection);
573         } else if (!category.categoryId().isEmpty()) {
574             m_categories.insert(category.categoryId(), category);
575             QStringList children = m_childCategories.value(parentId);
576 
577             for (QStringList &c : m_childCategories)
578                 c.removeAll(category.categoryId());
579 
580             if (!children.contains(category.categoryId())) {
581                 children.append(category.categoryId());
582                 m_childCategories.insert(parentId, children);
583             }
584             reply->setId(category.categoryId());
585         } else {
586             QPlaceCategory c = category;
587             c.setCategoryId(QUuid::createUuid().toString());
588             m_categories.insert(c.categoryId(), c);
589             QStringList children = m_childCategories.value(parentId);
590             if (!children.contains(c.categoryId())) {
591                 children.append(c.categoryId());
592                 m_childCategories.insert(parentId, children);
593             }
594 
595             reply->setId(c.categoryId());
596         }
597 
598         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
599 
600         return reply;
601     }
602 
removeCategory(const QString & categoryId)603     QPlaceIdReply *removeCategory(const QString &categoryId) override
604     {
605         IdReply *reply = new IdReply(QPlaceIdReply::RemoveCategory, this);
606         reply->setId(categoryId);
607 
608         if (!m_categories.contains(categoryId)) {
609             reply->setError(QPlaceReply::CategoryDoesNotExistError, tr("Category does not exist"));
610             QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection);
611         } else {
612             m_categories.remove(categoryId);
613 
614             for (auto &c : m_childCategories)
615                 c.removeAll(categoryId);
616         }
617 
618         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
619 
620         return reply;
621     }
622 
initializeCategories()623     QPlaceReply *initializeCategories() override
624     {
625         QPlaceReply *reply = new PlaceReply(this);
626 
627         QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection);
628 
629         return reply;
630     }
631 
parentCategoryId(const QString & categoryId)632     QString parentCategoryId(const QString &categoryId) const override
633     {
634         for (auto i = m_childCategories.cbegin(), end = m_childCategories.cend(); i != end; ++i) {
635             if (i.value().contains(categoryId))
636                 return i.key();
637         }
638 
639         return QString();
640     }
641 
childCategoryIds(const QString & categoryId)642     virtual QStringList childCategoryIds(const QString &categoryId) const override
643     {
644         return m_childCategories.value(categoryId);
645     }
646 
category(const QString & categoryId)647     virtual QPlaceCategory category(const QString &categoryId) const override
648     {
649         return m_categories.value(categoryId);
650     }
651 
childCategories(const QString & parentId)652     QList<QPlaceCategory> childCategories(const QString &parentId) const override
653     {
654         QList<QPlaceCategory> categories;
655 
656         foreach (const QString &id, m_childCategories.value(parentId))
657             categories.append(m_categories.value(id));
658 
659         return categories;
660     }
661 
locales()662     QList<QLocale> locales() const override
663     {
664         return m_locales;
665     }
666 
setLocales(const QList<QLocale> & locales)667     void setLocales(const QList<QLocale> &locales) override
668     {
669         m_locales = locales;
670     }
671 
constructIconUrl(const QPlaceIcon & icon,const QSize & size)672     QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override
673     {
674         QList<QPair<int, QUrl> > candidates;
675 
676         QMap<QString, int> sizeDictionary;
677         sizeDictionary.insert(QStringLiteral("s"), 20);
678         sizeDictionary.insert(QStringLiteral("m"), 30);
679         sizeDictionary.insert(QStringLiteral("l"), 50);
680 
681         QStringList sizeKeys;
682         sizeKeys << QStringLiteral("s") << QStringLiteral("m") << QStringLiteral("l");
683 
684         foreach (const QString &sizeKey, sizeKeys)
685         {
686             if (icon.parameters().contains(sizeKey))
687                 candidates.append(QPair<int, QUrl>(sizeDictionary.value(sizeKey),
688                                   icon.parameters().value(sizeKey).toUrl()));
689         }
690 
691         if (candidates.isEmpty())
692             return QUrl();
693         else if (candidates.count() == 1) {
694             return candidates.first().second;
695         } else {
696             //we assume icons are squarish so we can use height to
697             //determine which particular icon to return
698             int requestedHeight = size.height();
699 
700             for (int i = 0; i < candidates.count() - 1; ++i) {
701                 int thresholdHeight = (candidates.at(i).first + candidates.at(i+1).first) / 2;
702                 if (requestedHeight < thresholdHeight)
703                     return candidates.at(i).second;
704             }
705             return candidates.last().second;
706         }
707     }
708 
compatiblePlace(const QPlace & original)709     QPlace compatiblePlace(const QPlace &original) const override
710     {
711         QPlace place;
712         place.setName(original.name());
713         return place;
714     }
715 
716 private:
717     QList<QLocale> m_locales;
718     QHash<QString, QPlace> m_places;
719     QHash<QString, QPlaceCategory> m_categories;
720     QHash<QString, QStringList> m_childCategories;
721     QHash<QString, QStringList> m_placeRecommendations;
722     QHash<QString, QList<QPlaceReview> > m_placeReviews;
723     QHash<QString, QList<QPlaceImage> > m_placeImages;
724     QHash<QString, QList<QPlaceEditorial> > m_placeEditorials;
725 };
726 
727 #endif
728