Revision control

++ // Copyright (C) 2015 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "qgeoroutexmlparser.h"
++
++ #include <QXmlStreamReader>
++ #include <QStringList>
++ #include <QString>
++ #include <QtCore/QThreadPool>
++ #include <QDebug>
++
++ #include <QtPositioning/QGeoRectangle>
++ #include <QtPositioning/QGeoPath>
++ #include <QtLocation/QGeoRoute>
++ #include <QtLocation/private/qgeoroutesegment_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ QGeoDynamicSpeedInfoContainer::QGeoDynamicSpeedInfoContainer()
++ : trafficSpeed(0)
++ , baseSpeed(0)
++ , trafficTime(0)
++ , baseTime(0)
++ {}
++
++ QGeoRouteXmlParser::QGeoRouteXmlParser(const QGeoRouteRequest &request)
++ : m_request(request)
++ {
++ }
++
++ QGeoRouteXmlParser::~QGeoRouteXmlParser()
++ {
++ }
++
++ void QGeoRouteXmlParser::parse(const QByteArray &data)
++ {
++ m_data = data;
++ // QFile file("/tmp/here.xml");
++ // file.open(QIODevice::WriteOnly);
++ // file.write(data);
++ // file.close();
++ QThreadPool::globalInstance()->start(this);
++ }
++
++ void QGeoRouteXmlParser::run()
++ {
++ m_reader = new QXmlStreamReader(m_data);
++
++ if (!parseRootElement())
++ emit errorOccurred(m_reader->errorString());
++ else
++ emit results(m_results);
++
++ delete m_reader;
++ m_reader = 0;
++ }
++
++ bool QGeoRouteXmlParser::parseRootElement()
++ {
++ if (!m_reader->readNextStartElement()) {
++ m_reader->raiseError(QStringLiteral("Expected a root element named \"CalculateRoute\" "
++ "(no root element found)."));
++ return false;
++ }
++
++ if (m_reader->name() == QLatin1String("Error")) {
++ QXmlStreamAttributes attributes = m_reader->attributes();
++ if (attributes.value(QStringLiteral("type")) == QLatin1String("ApplicationError")
++ && attributes.value("subtype") == QLatin1String("NoRouteFound"))
++ return true;
++ }
++
++ bool updateroute = false;
++ if (m_reader->name() != QLatin1String("CalculateRoute")
++ && m_reader->name() != QLatin1String("GetRoute")) {
++ m_reader->raiseError(QStringLiteral("The root element is expected to have the name "
++ "\"CalculateRoute\" or \"GetRoute\" (root element "
++ "was named \"%1\").").arg(m_reader->name().toString()));
++ return false;
++ } else if (m_reader->name() == QLatin1String("GetRoute")) {
++ updateroute = true;
++ }
++
++ if (m_reader->readNextStartElement()) {
++ if (m_reader->name() != QLatin1String("Response")) {
++ m_reader->raiseError(QStringLiteral("Expected a element named \"Response\" (element "
++ "was named \"%1\").").arg(m_reader->name().toString()));
++ return false;
++ }
++ }
++
++ while (m_reader->readNextStartElement() && !m_reader->hasError()) {
++ if (m_reader->name() == QLatin1String("Route")) {
++ QGeoRoute route;
++ route.setRequest(m_request);
++ if (updateroute)
++ route.setTravelMode(QGeoRouteRequest::TravelMode(int(m_request.travelModes())));
++ if (!parseRoute(&route))
++ continue; //route parsing failed move on to the next
++ m_results.append(route);
++ } else if (m_reader->name() == QLatin1String("Progress")) {
++ //TODO: updated route progress
++ m_reader->skipCurrentElement();
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++
++ return !m_reader->hasError();
++ }
++
++ bool QGeoRouteXmlParser::parseRoute(QGeoRoute *route)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QLatin1String("Route"));
++ m_maneuvers.clear();
++ // m_segments.clear();
++ m_legs.clear();
++ int legIndex = 0;
++ m_reader->readNext();
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QLatin1String("Route")) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QLatin1String("RouteId")) {
++ route->setRouteId(m_reader->readElementText());
++ }
++ //else if (m_reader->name() == "Waypoint") {
++ // succeeded = parseWaypoint(route);
++ //}
++ else if (m_reader->name() == QLatin1String("Mode")) {
++ if (!parseMode(route))
++ return false;
++ } else if (m_reader->name() == QLatin1String("Shape")) {
++ QString elementName = m_reader->name().toString();
++ QList<QGeoCoordinate> path;
++ if (!parseGeoPoints(m_reader->readElementText(), &path, elementName))
++ return false;
++ route->setPath(path);
++ } else if (m_reader->name() == QLatin1String("BoundingBox")) {
++ QGeoRectangle bounds;
++ if (!parseBoundingBox(bounds))
++ return false;
++ route->setBounds(bounds);
++ } else if (m_reader->name() == QLatin1String("Leg")) {
++ if (!parseLeg(legIndex++))
++ return false;
++ } else if (m_reader->name() == QLatin1String("Summary")) {
++ if (!parseSummary(route))
++ return false;
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ if (m_reader->hasError())
++ return false;
++
++ return postProcessRoute(route);
++ }
++
++ bool QGeoRouteXmlParser::parseLeg(int legIndex)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Leg"));
++ QGeoRoute leg;
++ leg.setLegIndex(legIndex);
++ m_reader->readNext();
++ QList<QGeoManeuverContainer> maneuvers;
++ QList<QGeoRouteSegmentContainer> links;
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QStringLiteral("Leg")) &&
++ !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QStringLiteral("Maneuver")) {
++ if (!parseManeuver(maneuvers))
++ return false;
++ }
++ // Currently unused, after requesting shape attribute in maneuvers.
++ // Links, however, contain additional info, such as speed limits, and might become needed in the future.
++ // else if (m_reader->name() == QStringLiteral("Link")) {
++ // if (!parseLink(links))
++ // return false;
++ // }
++ else if (m_reader->name() == QLatin1String("TravelTime")) {
++ leg.setTravelTime(qRound(m_reader->readElementText().toDouble()));
++ } else if (m_reader->name() == QLatin1String("Length")) {
++ leg.setDistance(m_reader->readElementText().toDouble());
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ if (m_reader->hasError())
++ return false;
++
++ m_legs << leg;
++ // m_segments << links;
++ m_maneuvers << maneuvers;
++ return true;
++ }
++
++ bool QGeoRouteXmlParser::postProcessRoute(QGeoRoute *route)
++ {
++ QList<QList<QGeoRouteSegment>> legSegments;
++ Q_ASSERT(m_maneuvers.size());
++
++
++ // Step 3: populate the linkMap, linkId -> linkContainer
++ for (qsizetype i = 0; i < m_maneuvers.size(); i++) {
++ legSegments << QList<QGeoRouteSegment>();
++ QList<QGeoRouteSegment> &segments = legSegments[i];
++ QList<QGeoManeuverContainer> &maneuvers = m_maneuvers[i];
++ for (qsizetype j = 0; j < m_maneuvers.at(i).size(); j++) {
++ QGeoManeuverContainer &maneuver = maneuvers[j];
++ QGeoRouteSegment segment;
++
++ QVariantMap extendedAttributes;
++ extendedAttributes["first"] = maneuver.first;
++ extendedAttributes["last"] = maneuver.last;
++ extendedAttributes["legIndex"] = i;
++ extendedAttributes["id"] = maneuver.id;
++ extendedAttributes["toLink"] = maneuver.toLink;
++ extendedAttributes["index"] = j;
++ maneuver.maneuver.setExtendedAttributes(extendedAttributes);
++
++ segment.setDistance(maneuver.maneuver.distanceToNextInstruction());
++ segment.setTravelTime(maneuver.maneuver.timeToNextInstruction());
++ segment.setPath(maneuver.path);
++ segment.setManeuver(maneuver.maneuver);
++ segments << segment;
++ }
++ }
++
++ // Step 7: connect all segments.
++ QGeoRouteSegment segment;
++ QGeoRouteSegment firstSegment;
++ for (auto &segments: legSegments) {
++ for (qsizetype j = 0; j < segments.size(); j++) {
++ if (segment.isValid()) {
++ segment.setNextRouteSegment(segments[j]);
++ } else {
++ firstSegment = segments[j];
++ }
++ segment = segments[j];
++ if (j == segments.size() - 1) {
++ QGeoRouteSegmentPrivate *sp = QGeoRouteSegmentPrivate::get(segment);
++ sp->setLegLastSegment(true);
++ }
++ }
++ }
++
++ if (firstSegment.isValid())
++ route->setFirstRouteSegment(firstSegment);
++
++ // Step 8: fill route legs.
++ for (qsizetype i = 0; i < m_legs.size(); i++) {
++ auto &leg = m_legs[i];
++ leg.setTravelMode(route->travelMode());
++ leg.setRequest(route->request());
++ leg.setOverallRoute(*route);
++ leg.setLegIndex(i);
++
++ leg.setFirstRouteSegment(legSegments[i].first());
++
++ // handle path
++ QList<QGeoCoordinate> path;
++ QGeoRouteSegment s = leg.firstRouteSegment();
++ while (s.isValid()) {
++ path.append(s.path());
++ if (s.isLegLastSegment())
++ break;
++ s = s.nextRouteSegment();
++ }
++ leg.setPath(path);
++ leg.setBounds(QGeoPath(path).boundingGeoRectangle());
++ }
++ route->setRouteLegs(m_legs);
++ m_legs.clear();
++ // m_segments.clear();
++ m_maneuvers.clear();
++ return true;
++ }
++
++ /*
++ bool QGeoRouteXmlParser::parseWaypoint(QGeoRoute *route)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Waypoint");
++ m_reader->readNext();
++ QList<QGeoCoordinate> path(route->pathSummary());
++
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Waypoint")) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == "MappedPosition") {
++ QGeoCoordinate coordinates;
++ if(!parseCoordinates(coordinates))
++ return false;
++ path.append(coordinates);
++ }
++ else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++ route->setPathSummary(path);
++ return true;
++ }
++ */
++
++ bool QGeoRouteXmlParser::parseMode(QGeoRoute *route)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QLatin1String("Mode"));
++ m_reader->readNext();
++
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QLatin1String("Mode")) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QLatin1String("TransportModes")) {
++ QString value = m_reader->readElementText();
++ if (value == "car")
++ route->setTravelMode(QGeoRouteRequest::CarTravel);
++ else if (value == "pedestrian")
++ route->setTravelMode(QGeoRouteRequest::PedestrianTravel);
++ else if (value == "publicTransport")
++ route->setTravelMode(QGeoRouteRequest::PublicTransitTravel);
++ else if (value == "bicycle")
++ route->setTravelMode(QGeoRouteRequest::BicycleTravel);
++ else if (value == "truck")
++ route->setTravelMode(QGeoRouteRequest::TruckTravel);
++ else {
++ // unsupported mode
++ m_reader->raiseError(QString("Unsupported travel mode '\"%1\"'").arg(value));
++ return false;
++ }
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++ return !m_reader->hasError();
++ }
++
++ bool QGeoRouteXmlParser::parseSummary(QGeoRoute *route)
++ {
++ Q_ASSERT(route);
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QLatin1String("Summary"));
++ m_reader->readNext();
++
++ double baseTime = -1, trafficTime = -1;
++
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QLatin1String("Summary")) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QLatin1String("Distance")) {
++ route->setDistance(m_reader->readElementText().toDouble());
++ } else if (m_reader->name() == QLatin1String("TrafficTime")) {
++ trafficTime = m_reader->readElementText().toDouble();
++ } else if (m_reader->name() == QLatin1String("BaseTime")) {
++ baseTime = m_reader->readElementText().toDouble();
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ if (m_reader->hasError())
++ return false;
++
++ if (trafficTime >= 0)
++ route->setTravelTime(trafficTime);
++ else if (baseTime >= 0)
++ route->setTravelTime(baseTime);
++
++ return true;
++ }
++
++ bool QGeoRouteXmlParser::parseCoordinates(QGeoCoordinate &coord)
++ {
++ QString currentElement = m_reader->name().toString();
++ m_reader->readNext();
++
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == currentElement) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ QString name = m_reader->name().toString();
++ QString value = m_reader->readElementText();
++ if (name == "Latitude")
++ coord.setLatitude(value.toDouble());
++ else if (name == "Longitude")
++ coord.setLongitude(value.toDouble());
++ }
++ m_reader->readNext();
++ }
++
++ return !m_reader->hasError();
++ }
++
++ bool QGeoRouteXmlParser::parseManeuver(QList<QGeoManeuverContainer> &maneuvers)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QLatin1String("Maneuver"));
++
++ if (!m_reader->attributes().hasAttribute("id")) {
++ m_reader->raiseError("The element \"Maneuver\" did not have the required attribute \"id\".");
++ return false;
++ }
++ QGeoManeuverContainer maneuverContainter;
++ maneuverContainter.id = m_reader->attributes().value("id").toString();
++
++ m_reader->readNext();
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QLatin1String("Maneuver")) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QLatin1String("Position")) {
++ QGeoCoordinate coordinates;
++ if (parseCoordinates(coordinates))
++ maneuverContainter.maneuver.setPosition(coordinates);
++ } else if (m_reader->name() == QLatin1String("Instruction")) {
++ maneuverContainter.maneuver.setInstructionText(m_reader->readElementText());
++ } else if (m_reader->name() == QLatin1String("Shape")) {
++ QString elementName = m_reader->name().toString();
++ QList<QGeoCoordinate> path;
++ if (!parseGeoPoints(m_reader->readElementText(), &path, elementName))
++ return false;
++ maneuverContainter.path = path;
++ } else if (m_reader->name() == QLatin1String("ToLink")) {
++ maneuverContainter.toLink = m_reader->readElementText();
++ } else if (m_reader->name() == QLatin1String("TravelTime")) {
++ maneuverContainter.maneuver.setTimeToNextInstruction(qRound(m_reader->readElementText().toDouble()));
++ } else if (m_reader->name() == QLatin1String("Length")) {
++ maneuverContainter.maneuver.setDistanceToNextInstruction(m_reader->readElementText().toDouble());
++ } else if (m_reader->name() == QLatin1String("Direction")) {
++ QString value = m_reader->readElementText();
++ if (value == "forward")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionForward);
++ else if (value == "bearRight")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionBearRight);
++ else if (value == "lightRight")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLightRight);
++ else if (value == "right")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionRight);
++ else if (value == "hardRight")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionHardRight);
++ else if (value == "uTurnRight")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionUTurnRight);
++ else if (value == "uTurnLeft")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionUTurnLeft);
++ else if (value == "hardLeft")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionHardLeft);
++ else if (value == "left")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLeft);
++ else if (value == "lightLeft")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLightLeft);
++ else if (value == "bearLeft")
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionBearLeft);
++ else
++ maneuverContainter.maneuver.setDirection(QGeoManeuver::NoDirection);
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ if (m_reader->hasError())
++ return false;
++
++ maneuvers.append(maneuverContainter);
++ return true;
++ }
++
++ bool QGeoRouteXmlParser::parseLink(QList<QGeoRouteSegmentContainer> &links)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Link"));
++ m_reader->readNext();
++
++ QGeoRouteSegmentContainer segmentContainer;
++
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QStringLiteral("Link")) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QStringLiteral("LinkId")) {
++ segmentContainer.id = m_reader->readElementText();
++ } else if (m_reader->name() == QStringLiteral("Shape")) {
++ QString elementName = m_reader->name().toString();
++ QList<QGeoCoordinate> path;
++ parseGeoPoints(m_reader->readElementText(), &path, elementName);
++ segmentContainer.segment.setPath(path);
++ } else if (m_reader->name() == QStringLiteral("Length")) {
++ segmentContainer.segment.setDistance(m_reader->readElementText().toDouble());
++ } else if (m_reader->name() == QStringLiteral("Maneuver")) {
++ segmentContainer.maneuverId = m_reader->readElementText();
++ } else if (m_reader->name() == QStringLiteral("DynamicSpeedInfo")) {
++ QGeoDynamicSpeedInfoContainer speedInfo;
++ if (!parseDynamicSpeedInfo(speedInfo))
++ return false;
++ const double time = speedInfo.trafficTime >= 0
++ ? speedInfo.trafficTime : speedInfo.baseTime;
++ if (time >= 0)
++ segmentContainer.segment.setTravelTime(time);
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ if (m_reader->hasError())
++ return false;
++ links.append(segmentContainer);
++ return true;
++ }
++
++ bool QGeoRouteXmlParser::parseGeoPoints(const QString &strPoints, QList<QGeoCoordinate> *geoPoints,
++ const QString &elementName)
++ {
++ const QStringList rawPoints = strPoints.split(' ');
++
++ for (const auto &rawPoint : rawPoints) {
++ const QStringList coords = rawPoint.split(',');
++
++ if (coords.length() != 2) {
++ m_reader->raiseError(QStringLiteral("Each of the space separated values of \"%1\" "
++ "is expected to be a comma separated pair of "
++ "coordinates (value was \"%2\")")
++ .arg(elementName).arg(rawPoint));
++ return false;
++ }
++
++ bool ok = false;
++ const QString latString = coords[0];
++ double lat = latString.toDouble(&ok);
++
++ if (!ok) {
++ m_reader->raiseError(QStringLiteral("The latitude portions of \"%1\" are expected to "
++ "have a value convertable to a double (value was "
++ "\"%2\")").arg(elementName).arg(latString));
++ return false;
++ }
++
++ const QString lngString = coords[1];
++ double lng = lngString.toDouble(&ok);
++
++ if (!ok) {
++ m_reader->raiseError(QStringLiteral("The longitude portions of \"%1\" are expected to "
++ "have a value convertable to a double (value was "
++ "\"%2\")").arg(elementName).arg(lngString));
++ return false;
++ }
++
++ QGeoCoordinate geoPoint(lat, lng);
++ geoPoints->append(geoPoint);
++ }
++
++ return true;
++ }
++
++ bool QGeoRouteXmlParser::parseBoundingBox(QGeoRectangle &bounds)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QLatin1String("BoundingBox"));
++
++ QGeoCoordinate tl;
++ QGeoCoordinate br;
++
++ m_reader->readNext();
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
++ && m_reader->name() == QLatin1String("BoundingBox")) && !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QLatin1String("TopLeft")) {
++ QGeoCoordinate coordinates;
++ if (parseCoordinates(coordinates))
++ tl = coordinates;
++ } else if (m_reader->name() == QLatin1String("BottomRight")) {
++ QGeoCoordinate coordinates;
++ if (parseCoordinates(coordinates))
++ br = coordinates;
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ if (m_reader->hasError())
++ return false;
++
++ if (tl.isValid() && br.isValid()) {
++ bounds = QGeoRectangle(tl, br);
++ return true;
++ }
++
++ return false;
++ }
++
++ bool QGeoRouteXmlParser::parseDynamicSpeedInfo(QGeoDynamicSpeedInfoContainer &speedInfo)
++ {
++ Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("DynamicSpeedInfo"));
++
++ m_reader->readNext();
++ while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == QStringLiteral("DynamicSpeedInfo")) &&
++ !m_reader->hasError()) {
++ if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
++ if (m_reader->name() == QStringLiteral("TrafficSpeed")) {
++ speedInfo.trafficSpeed = m_reader->readElementText().toDouble();
++ } else if (m_reader->name() == QStringLiteral("TrafficTime")) {
++ speedInfo.trafficTime = qRound(m_reader->readElementText().toDouble());
++ } else if (m_reader->name() == QStringLiteral("BaseSpeed")) {
++ speedInfo.baseSpeed = m_reader->readElementText().toDouble();
++ } else if (m_reader->name() == QStringLiteral("BaseTime")) {
++ speedInfo.baseTime = qRound(m_reader->readElementText().toDouble());
++ } else {
++ m_reader->skipCurrentElement();
++ }
++ }
++ m_reader->readNext();
++ }
++
++ return !m_reader->hasError();
++ }
++
++ QT_END_NAMESPACE