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 QtLocation module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qplacedetailsreplyimpl.h"
38 #include "jsonparserhelpers.h"
39 #include "../qplacemanagerengine_nokiav2.h"
40 #include "../qgeoerror_messages.h"
41 
42 #include <QCoreApplication>
43 #include <QtCore/QJsonDocument>
44 #include <QtCore/QJsonObject>
45 #include <QtCore/QJsonArray>
46 #include <QtNetwork/QNetworkReply>
47 #include <QtLocation/QPlaceManager>
48 #include <QtLocation/QPlaceSupplier>
49 #include <QtLocation/QPlaceImage>
50 #include <QtLocation/QPlaceEditorial>
51 #include <QtLocation/QPlaceReview>
52 #include <QtLocation/QPlaceUser>
53 
54 QT_BEGIN_NAMESPACE
55 
56 // These countries format the street address as: {house number} {street name}
57 // All other countries format it as: {street name} {house number}
58 static const char COUNTRY_TABLE_string[] =
59     "CAN\0"
60     "NZL\0"
61     "GBR\0"
62     "AUS\0"
63     "LKA\0"
64     "USA\0"
65     "SGP\0"
66     "FRA\0"
67     "BHS\0"
68     "CHN\0"
69     "IND\0"
70     "IRL\0"
71     "ARE\0"
72     "\0";
73 
74 static const int COUNTRY_TABLE_indices[] = {
75        0,    4,    8,   12,   16,   20,   24,   28,
76       32,   36,   40,   44,   48,   -1
77 };
78 
countryTableContains(const QString & countryCode)79 static bool countryTableContains(const QString &countryCode)
80 {
81     for (int i = 0; COUNTRY_TABLE_indices[i] != -1; ++i) {
82         if (countryCode == QLatin1String(COUNTRY_TABLE_string + COUNTRY_TABLE_indices[i]))
83             return true;
84     }
85 
86     return false;
87 }
88 
QPlaceDetailsReplyImpl(QNetworkReply * reply,QPlaceManagerEngineNokiaV2 * parent)89 QPlaceDetailsReplyImpl::QPlaceDetailsReplyImpl(QNetworkReply *reply,
90                                                QPlaceManagerEngineNokiaV2 *parent)
91 :   QPlaceDetailsReply(parent), m_engine(parent)
92 {
93     if (!reply) {
94         setError(UnknownError, QStringLiteral("Null reply"));
95         return;
96     }
97     connect(reply, SIGNAL(finished()), this, SLOT(replyFinished()));
98     connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)),
99             this, SLOT(replyError(QNetworkReply::NetworkError)));
100     connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort);
101     connect(this, &QObject::destroyed, reply, &QObject::deleteLater);
102 }
103 
~QPlaceDetailsReplyImpl()104 QPlaceDetailsReplyImpl::~QPlaceDetailsReplyImpl()
105 {
106 }
107 
setError(QPlaceReply::Error error_,const QString & errorString)108 void QPlaceDetailsReplyImpl::setError(QPlaceReply::Error error_, const QString &errorString)
109 {
110     QPlaceReply::setError(error_, errorString);
111     emit error(error_, errorString);
112     setFinished(true);
113     emit finished();
114 }
115 
replyFinished()116 void QPlaceDetailsReplyImpl::replyFinished()
117 {
118     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
119     reply->deleteLater();
120 
121     if (reply->error() != QNetworkReply::NoError)
122         return;
123 
124     QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
125     if (!document.isObject()) {
126         setError(ParseError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR));
127         return;
128     }
129 
130     QJsonObject object = document.object();
131 
132     QPlace place;
133 
134     place.setPlaceId(object.value(QLatin1String("placeId")).toString());
135 
136     //const QUrl view = object.value(QLatin1String("view")).toString();
137 
138     place.setName(object.value(QLatin1String("name")).toString());
139 
140     //if (object.contains(QLatin1String("distance")))
141     //    double distance = object.value(QLatin1String("distance")).toDouble();
142 
143     //if (object.contains(QLatin1String("alternativeNames"))) {
144     //    QJsonArray alternativeNames = object.value(QLatin1String("alternativeNames")).toArray();
145     //}
146 
147     QGeoLocation location;
148 
149     QJsonObject locationObject = object.value(QLatin1String("location")).toObject();
150 
151     //if (locationObject.contains(QLatin1String("locationId")))
152     //    const QString locationId = locationObject.value(QLatin1String("locationId")).toString();
153 
154     QJsonArray position = locationObject.value(QLatin1String("position")).toArray();
155     location.setCoordinate(QGeoCoordinate(position.at(0).toDouble(), position.at(1).toDouble()));
156 
157     QGeoAddress address;
158 
159     QJsonObject addressObject = locationObject.value(QLatin1String("address")).toObject();
160 
161     address.setText(addressObject.value(QLatin1String("text")).toString());
162 
163     address.setCountry(addressObject.value(QLatin1String("country")).toString());
164     address.setCountryCode(addressObject.value(QLatin1String("countryCode")).toString());
165 
166     QString house;
167     QString street;
168 
169     if (addressObject.contains(QLatin1String("house")))
170         house = addressObject.value(QLatin1String("house")).toString();
171     if (addressObject.contains(QLatin1String("street")))
172         street = addressObject.value(QLatin1String("street")).toString();
173 
174     if (countryTableContains(address.countryCode())) {
175         if (!house.isEmpty() && !street.startsWith(house))
176             street = house + QLatin1Char(' ') + street;
177     } else {
178         if (!house.isEmpty() && !street.endsWith(house))
179             street += QLatin1Char(' ') + house;
180     }
181 
182     address.setStreet(street);
183 
184     if (addressObject.contains(QLatin1String("city")))
185         address.setCity(addressObject.value(QLatin1String("city")).toString());
186     if (addressObject.contains(QLatin1String("district")))
187         address.setDistrict(addressObject.value(QLatin1String("district")).toString());
188     if (addressObject.contains(QLatin1String("state")))
189         address.setState(addressObject.value(QLatin1String("state")).toString());
190     if (addressObject.contains(QLatin1String("county")))
191         address.setCounty(addressObject.value(QLatin1String("county")).toString());
192     if (addressObject.contains(QLatin1String("postalCode")))
193         address.setPostalCode(addressObject.value(QLatin1String("postalCode")).toString());
194 
195     location.setAddress(address);
196 
197     if (locationObject.contains(QLatin1String("bbox"))) {
198         QJsonArray bbox = locationObject.value(QLatin1String("bbox")).toArray();
199         QGeoRectangle box(QGeoCoordinate(bbox.at(3).toDouble(), bbox.at(0).toDouble()),
200                             QGeoCoordinate(bbox.at(1).toDouble(), bbox.at(2).toDouble()));
201         location.setBoundingBox(box);
202     }
203 
204     place.setLocation(location);
205 
206     place.setCategories(parseCategories(object.value(QLatin1String("categories")).toArray(),
207                                         m_engine));
208 
209     place.setIcon(m_engine->icon(object.value(QLatin1String("icon")).toString(),
210                                  place.categories()));
211 
212     if (object.contains(QLatin1String("contacts"))) {
213         QJsonObject contactsObject = object.value(QLatin1String("contacts")).toObject();
214 
215         if (contactsObject.contains(QLatin1String("phone"))) {
216             place.setContactDetails(QPlaceContactDetail::Phone,
217                                     parseContactDetails(contactsObject.value(QLatin1String("phone")).toArray()));
218         }
219         if (contactsObject.contains(QLatin1String("fax"))) {
220             place.setContactDetails(QPlaceContactDetail::Fax,
221                                     parseContactDetails(contactsObject.value(QLatin1String("fax")).toArray()));
222         }
223         if (contactsObject.contains(QLatin1String("website"))) {
224             place.setContactDetails(QPlaceContactDetail::Website,
225                                     parseContactDetails(contactsObject.value(QLatin1String("website")).toArray()));
226         }
227         if (contactsObject.contains(QLatin1String("email"))) {
228             place.setContactDetails(QPlaceContactDetail::Email,
229                                     parseContactDetails(contactsObject.value(QLatin1String("email")).toArray()));
230         }
231     }
232 
233     //if (object.contains(QLatin1String("verifiedByOwner")))
234     //    bool verifiedByOwner = object.value(QLatin1String("verifiedByOwner")).toBool();
235 
236     if (object.contains(QLatin1String("attribution")))
237         place.setAttribution(object.value(QLatin1String("attribution")).toString());
238 
239     if (object.contains(QLatin1String("supplier"))) {
240         place.setSupplier(parseSupplier(object.value(QLatin1String("supplier")).toObject(),
241                                         m_engine));
242     }
243 
244     if (object.contains(QLatin1String("ratings"))) {
245         QJsonObject ratingsObject = object.value(QLatin1String("ratings")).toObject();
246 
247         QPlaceRatings ratings;
248         ratings.setAverage(ratingsObject.value(QLatin1String("average")).toDouble());
249         ratings.setCount(ratingsObject.value(QLatin1String("count")).toDouble());
250         ratings.setMaximum(5.0);
251 
252         place.setRatings(ratings);
253     }
254 
255     if (object.contains(QLatin1String("extended"))) {
256         QJsonObject extendedObject = object.value(QLatin1String("extended")).toObject();
257 
258         for (auto it = extendedObject.constBegin(), end = extendedObject.constEnd(); it != end; ++it) {
259             QJsonObject attributeObject = it.value().toObject();
260 
261             QPlaceAttribute attribute;
262 
263             attribute.setLabel(attributeObject.value(QLatin1String("label")).toString());
264             attribute.setText(attributeObject.value(QLatin1String("text")).toString());
265 
266             QString key = it.key();
267             if (key == QLatin1String("payment"))
268                 place.setExtendedAttribute(QPlaceAttribute::Payment, attribute);
269             else if (key == QLatin1String("openingHours"))
270                 place.setExtendedAttribute(QPlaceAttribute::OpeningHours, attribute);
271             else
272                 place.setExtendedAttribute(key, attribute);
273         }
274     }
275 
276     if (object.contains(QLatin1String("media"))) {
277         QJsonObject mediaObject = object.value(QLatin1String("media")).toObject();
278 
279         if (mediaObject.contains(QLatin1String("images"))) {
280             QPlaceContent::Collection collection;
281             int totalCount = 0;
282 
283             parseCollection(QPlaceContent::ImageType,
284                             mediaObject.value(QLatin1String("images")).toObject(),
285                             &collection, &totalCount, 0, 0, m_engine);
286 
287             place.setTotalContentCount(QPlaceContent::ImageType, totalCount);
288             place.setContent(QPlaceContent::ImageType, collection);
289         }
290         if (mediaObject.contains(QLatin1String("editorials"))) {
291             QPlaceContent::Collection collection;
292             int totalCount = 0;
293 
294             parseCollection(QPlaceContent::EditorialType,
295                             mediaObject.value(QLatin1String("editorials")).toObject(),
296                             &collection, &totalCount, 0, 0, m_engine);
297 
298             place.setTotalContentCount(QPlaceContent::EditorialType, totalCount);
299             place.setContent(QPlaceContent::EditorialType, collection);
300         }
301         if (mediaObject.contains(QLatin1String("reviews"))) {
302             QPlaceContent::Collection collection;
303             int totalCount = 0;
304 
305             parseCollection(QPlaceContent::ReviewType,
306                             mediaObject.value(QLatin1String("reviews")).toObject(),
307                             &collection, &totalCount, 0, 0, m_engine);
308 
309             place.setTotalContentCount(QPlaceContent::ReviewType, totalCount);
310             place.setContent(QPlaceContent::ReviewType, collection);
311         }
312     }
313 
314     //if (object.contains(QLatin1String("related"))) {
315     //    QJsonObject relatedObject = object.value(QLatin1String("related")).toObject();
316     //}
317 
318     QPlaceAttribute provider;
319     provider.setText(QLatin1String("here"));
320     place.setExtendedAttribute(QPlaceAttribute::Provider, provider);
321 
322     place.setVisibility(QLocation::PublicVisibility);
323     place.setDetailsFetched(true);
324     setPlace(place);
325 
326     setFinished(true);
327     emit finished();
328 }
329 
replyError(QNetworkReply::NetworkError error)330 void QPlaceDetailsReplyImpl::replyError(QNetworkReply::NetworkError error)
331 {
332     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
333     reply->deleteLater();
334     if (error == QNetworkReply::OperationCanceledError) {
335         setError(QPlaceReply::CancelError, QStringLiteral("Request cancelled"));
336     } else if (error == QNetworkReply::ContentNotFoundError) {
337         setError(QPlaceReply::PlaceDoesNotExistError,
338                  QString::fromLatin1("The id, %1, does not reference an existing place")
339                  .arg(m_placeId));
340     } else {
341         setError(QPlaceReply::CommunicationError, reply->errorString());
342     }
343 }
344 
345 QT_END_NAMESPACE
346