1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
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 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "servicemetadata_p.h"
35#include <QFile>
36#include <QDataStream>
37#include <QDebug>
38#include "qserviceinterfacedescriptor_p.h"
39
40//XML tags and attributes
41//General
42#define NAME_TAG "name"
43#define DESCRIPTION_TAG "description"
44#define SERVICEFW_TAG "SFW"
45#define XML_MAX "1.1"
46
47//Service related
48#define SERVICE_TAG "service"
49#define SERVICE_FILEPATH "filepath"
50#define SERVICE_IPCADDRESS "ipcaddress"
51
52//Interface related
53#define INTERFACE_TAG "interface"
54#define INTERFACE_VERSION "version"
55#define INTERFACE_CAPABILITY "capabilities"
56#define INTERFACE_CUSTOM_PROPERTY "customproperty"
57
58//Service type prefix
59#define SERVICE_IPC_PREFIX "_q_ipc_addr:"
60
61QT_BEGIN_NAMESPACE
62
63#ifndef QT_NO_DATASTREAM
64SERVICEMETADATA_EXPORT QDataStream &operator<<(QDataStream &out, const ServiceMetaDataResults &r)
65{
66 out << r.type << r.name << r.location;
67 out << r.description << r.interfaces << r.latestInterfaces;
68
69 return out;
70}
71
72SERVICEMETADATA_EXPORT QDataStream &operator>>(QDataStream &in, ServiceMetaDataResults &r)
73{
74 in >> r.type >> r.name >> r.location;
75 in >> r.description >> r.interfaces >> r.latestInterfaces;
76
77 return in;
78}
79#endif
80
81/*
82 \class ServiceMetaData
83
84
85 Utility class (used by service database) that offers support for
86 parsing metadata service xml registry file during service registration. \n
87
88 It uses QXMLStreamReader class for parsing. Supproted Operations are:
89 - Parse the service and interfaces defined in XML file
90 - name, version, capabilitiesList, description and filePath of service can be retrieved
91 - each interface can be retrieved
92*/
93
94/*
95 * Class constructor
96 *
97 * @param aXmlFilePath path to the xml file that describes the service.
98 */
99ServiceMetaData::ServiceMetaData(const QString &aXmlFilePath)
100 : xmlDevice(new QFile(aXmlFilePath)),
101 ownsXmlDevice(true),
102 serviceType(QService::Plugin),
103 latestError(0)
104{}
105
106/*
107 * Class constructor
108 *
109 * @param device QIODevice that contains the XML data that describes the service.
110 */
111ServiceMetaData::ServiceMetaData(QIODevice *device)
112 : xmlDevice(device),
113 ownsXmlDevice(false),
114 serviceType(QService::Plugin),
115 latestError(0)
116{}
117
118/*
119 * Class destructor
120 *
121 */
122ServiceMetaData::~ServiceMetaData()
123{
124 if (ownsXmlDevice)
125 delete xmlDevice;
126}
127
128/*
129 Sets the device containing the XML data that describes the service to \a device.
130 */
131void ServiceMetaData::setDevice(QIODevice *device)
132{
133 clearMetadata();
134 xmlDevice = device;
135 ownsXmlDevice = false;
136}
137
138/*
139 Returns the device containing the XML data that describes the service.
140*/
141QIODevice *ServiceMetaData::device() const
142{
143 return xmlDevice;
144}
145
146/*
147 * Gets the service name
148 *
149 * @return service name or default value (empty string) if it is not available
150 */
151/*QString ServiceMetaData::name() const
152{
153 return serviceName;
154}*/
155
156/*
157 * Gets the path of the service implementation file
158 *
159 * @return service implementation filepath
160 */
161/*QString ServiceMetaData::location() const
162{
163 return serviceLocation;
164}*/
165
166/*
167 * Gets the service description
168 *
169 * @return service description or default value (empty string) if it is not available
170 */
171/*QString ServiceMetaData::description() const
172{
173 return serviceDescription;
174}*/
175
176/*
177 Returns the metadata of the interace at \a index; otherwise
178 returns 0.
179 */
180/*QList<QServiceInterfaceDescriptor> ServiceMetaData::getInterfaces() const
181{
182 return serviceInterfaces;
183} */
184
185/*!
186 \internal
187
188 Returns a streamable object containing the results of the parsing.
189*/
190ServiceMetaDataResults ServiceMetaData::parseResults() const
191{
192 ServiceMetaDataResults results;
193 results.type = serviceType;
194 results.location = serviceLocation;
195 results.name = serviceName;
196 results.description = serviceDescription;
197 results.interfaces = serviceInterfaces;
198 results.latestInterfaces = latestInterfaces();
199
200 return results;
201}
202
203/*
204 Parses the file and extracts the service metadata \n
205 Custom error codes: \n
206 SFW_ERROR_UNABLE_TO_OPEN_FILE in case can not open the XML file \n
207 SFW_ERROR_INVALID_XML_FILE in case service registry is not a valid XML file \n
208 SFW_ERROR_NO_SERVICE in case XML file has no service tag\n
209 @return true if the metadata was read properly, false if there is an error
210 */
211bool ServiceMetaData::extractMetadata()
212{
213 Q_ASSERT(checkVersion(QLatin1String(XML_MAX)));
214
215 latestError = 0;
216 clearMetadata();
217 QXmlStreamReader xmlReader;
218 bool parseError = false;
219 //Open xml file
220 if (!xmlDevice->isOpen() && !xmlDevice->open(mode: QIODevice::ReadOnly)) {
221 qWarning() << xmlDevice->errorString();
222 latestError = ServiceMetaData::SFW_ERROR_UNABLE_TO_OPEN_FILE;
223 parseError = true;
224 } else {
225 //Load xml content
226 xmlReader.setDevice(xmlDevice);
227 // Read XML doc
228 while (!xmlReader.atEnd() && !parseError) {
229 xmlReader.readNext();
230 //Found <SFW> xml versioning tag introduced in 1.1
231 //If this tag is not found the XML parser version will be 1.0
232 if (xmlReader.isStartElement() && xmlReader.name() == QLatin1String(SERVICEFW_TAG)) {
233 if (!processVersionElement(aXMLReader&: xmlReader)) {
234 parseError = true;
235 }
236 }
237 //Found a <service> node, read service related metadata
238 else if (xmlReader.isStartElement() && xmlReader.name() == QLatin1String(SERVICE_TAG)) {
239 if (!processServiceElement(aXMLReader&: xmlReader)) {
240 parseError = true;
241 }
242 }
243 else if (xmlReader.isStartElement() && xmlReader.name() != QLatin1String(SERVICE_TAG)
244 && xmlReader.name() != QLatin1String(SERVICEFW_TAG)) {
245 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE;
246 parseError = true;
247 }
248 else if (xmlReader.tokenType() == QXmlStreamReader::Invalid) {
249 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE;
250 parseError = true;
251 }
252 }
253 if (ownsXmlDevice)
254 xmlDevice->close();
255 }
256
257 if (parseError) {
258 //provide better error reports
259 switch (latestError) {
260 case SFW_ERROR_NO_SERVICE: /* Can not find service root node in XML file*/
261 qDebug() << "Missing <service> tag";
262 break;
263 case SFW_ERROR_NO_SERVICE_NAME: /* Can not find service name in XML file */
264 qDebug() << "Missing or empty <name> tag within <service>";
265 break;
266 case SFW_ERROR_NO_SERVICE_PATH: /* Can not find service filepath or ipcaddress in XML file */
267 if (greaterThan(v1: xmlVersion, v2: QLatin1String("1.0")))
268 qDebug() << "Missing or empty <filepath> or <ipcaddress> tag within <service>";
269 else
270 qDebug() << "Missing or empty <filepath> tag within <service>";
271 break;
272 case SFW_ERROR_NO_SERVICE_INTERFACE: /* No interface for the service in XML file*/
273 qDebug() << "Missing <interface> tag";
274 break;
275 case SFW_ERROR_NO_INTERFACE_VERSION: /* Can not find interface version in XML file */
276 qDebug() << "Missing or empty <version> tag within <interface>";
277 break;
278 case SFW_ERROR_NO_INTERFACE_NAME: /* Can not find interface name in XML file*/
279 qDebug() << "Missing or empty <name> tag within <interface>";
280 break;
281 case SFW_ERROR_UNABLE_TO_OPEN_FILE: /* Error opening XML file*/
282 qDebug() << "Unable to open service xml file";
283 break;
284 case SFW_ERROR_INVALID_XML_FILE: /* Not a valid XML file*/
285 qDebug() << "Not a valid service xml";
286 break;
287 case SFW_ERROR_PARSE_SERVICE: /* Error parsing service node */
288 qDebug().nospace() << "Invalid tag within <service> with the supplied version("
289 << xmlVersion << ")";
290 break;
291 case SFW_ERROR_PARSE_INTERFACE: /* Error parsing interface node */
292 qDebug() << "Invalid tag within <interface> tags";
293 break;
294 case SFW_ERROR_DUPLICATED_INTERFACE: /* The same interface is defined twice */
295 qDebug() << "The same interface has been defined more than once";
296 break;
297 case SFW_ERROR_INVALID_VERSION:
298 qDebug() << "Invalid version string, expected: x.y";
299 break;
300 case SFW_ERROR_DUPLICATED_TAG: /* The tag appears twice */
301 qDebug() << "XML tag appears twice";
302 break;
303 case SFW_ERROR_INVALID_CUSTOM_TAG: /* The customproperty tag is not correctly formatted or otherwise incorrect*/
304 qDebug() << "Invalid custom property tag";
305 break;
306 case SFW_ERROR_DUPLICATED_CUSTOM_KEY: /* The customproperty appears twice*/
307 qDebug() << "Same custom property appears multiple times";
308 break;
309 case SFW_ERROR_MULTIPLE_SERVICE_TYPES: /* Both filepath and ipcaddress found in the XML file */
310 qDebug() << "Cannot specify both <filepath> and <ipcaddress> tags within <service>";
311 break;
312 case SFW_ERROR_INVALID_FILEPATH: /* Service path cannot contain IPC prefix */
313 qDebug() << "Invalid service location, avoid private prefixes";
314 break;
315 case SFW_ERROR_INVALID_XML_VERSION: /* Error parsing servicefw version node */
316 qDebug() << "Invalid or missing version attribute in <SFW> tag";
317 break;
318 case SFW_ERROR_UNSUPPORTED_IPC: /* Servicefw version doesn't support IPC */
319 qDebug().nospace() << "Supplied service framework version(" << xmlVersion
320 << ") doesn't support the <ipcaddress> tag";
321 break;
322 case SFW_ERROR_UNSUPPORTED_XML_VERSION: /* Unsupported servicefw version supplied */
323 qDebug().nospace() << "Service framework version(" << xmlVersion
324 << ") is higher than available support(" << QLatin1String(XML_MAX) << ")";
325 break;
326 }
327 clearMetadata();
328 }
329 return !parseError;
330}
331
332/*
333 Gets the latest parsing error \n
334 @return parsing error(negative value) or 0 in case there is none
335 */
336int ServiceMetaData::getLatestError() const
337{
338 return latestError;
339}
340
341/*
342 Parses the service framework xml version tag and continues to parse the service
343*/
344bool ServiceMetaData::processVersionElement(QXmlStreamReader &aXMLReader)
345{
346 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICEFW_TAG));
347 bool parseError = false;
348
349 if (aXMLReader.attributes().hasAttribute(qualifiedName: QLatin1String("version"))) {
350 xmlVersion = aXMLReader.attributes().value(qualifiedName: QLatin1String("version")).toString();
351 bool success = checkVersion(version: xmlVersion);
352
353 if (xmlVersion.isEmpty() || !success) {
354 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_VERSION;
355 parseError = true;
356 } else {
357 if (greaterThan(v1: xmlVersion, v2: QLatin1String(XML_MAX))) {
358 latestError = ServiceMetaData::SFW_ERROR_UNSUPPORTED_XML_VERSION;
359 parseError = true;
360 }
361 }
362 } else {
363 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_VERSION;
364 parseError = true;
365 }
366
367 while (!parseError && !aXMLReader.atEnd()) {
368 aXMLReader.readNext();
369 //Found a <service> node, read service related metadata
370 if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)) {
371 if (!processServiceElement(aXMLReader)) {
372 parseError = true;
373 }
374 }
375 else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(SERVICEFW_TAG)) {
376 //Found </SFW>, leave the loop
377 break;
378 }
379 else if (aXMLReader.isStartElement() && aXMLReader.name() != QLatin1String(SERVICE_TAG)) {
380 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE;
381 parseError = true;
382 }
383 else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) {
384 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE;
385 parseError = true;
386 }
387 }
388
389 return !parseError;
390}
391
392/*
393 Parses and extracts the service metadata from the current xml <service> node \n
394 */
395bool ServiceMetaData::processServiceElement(QXmlStreamReader &aXMLReader)
396{
397 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG));
398 bool parseError = false;
399
400 int dupSTags[4] = {0 //->tag name
401 ,0 //-> service description
402 ,0 //-> filepath
403 ,0 //-> ipcaddress
404 };
405 while (!parseError && !aXMLReader.atEnd()) {
406 aXMLReader.readNext();
407 if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(NAME_TAG)) {
408 //Found <name> tag
409 serviceName = aXMLReader.readElementText();
410 dupSTags[0]++;
411 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(DESCRIPTION_TAG)) {
412 //Found <description> tag
413 serviceDescription = aXMLReader.readElementText();
414 dupSTags[1]++;
415 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_FILEPATH) ) {
416 //Found <filepath> tag for plugin service
417 dupSTags[2]++;
418 serviceLocation = aXMLReader.readElementText();
419 //Check if IPC prefix was used incorrectly here
420 if (serviceLocation.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) {
421 latestError = ServiceMetaData::SFW_ERROR_INVALID_FILEPATH;
422 parseError = true;
423 }
424 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_IPCADDRESS) ) {
425 //Found <ipcaddress> tag for IPC service
426 //Check if servicefw XML version supports IPC
427 if (greaterThan(v1: xmlVersion, v2: QLatin1String("1.0"))) {
428 dupSTags[3]++;
429 serviceLocation = aXMLReader.readElementText();
430 //Check if IPC prefix was used incorrectly here
431 if (serviceLocation.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) {
432 latestError = ServiceMetaData::SFW_ERROR_INVALID_FILEPATH;
433 parseError = true;
434 }
435 } else {
436 latestError = ServiceMetaData::SFW_ERROR_UNSUPPORTED_IPC;
437 parseError = true;
438 }
439 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)) {
440 //Found interface> node, read module related metadata
441 if (!processInterfaceElement(aXMLReader))
442 parseError = true;
443 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String("version")) {
444 //Found <version> tag on service level. We ignore this for now
445 aXMLReader.readElementText();
446 } else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)) {
447 //Found </service>, leave the loop
448 break;
449 } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) {
450 latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE;
451 parseError = true;
452 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) {
453 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE;
454 parseError = true;
455 }
456 }
457
458 if ( !parseError ) {
459 if (serviceName.isEmpty()) {
460 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME;
461 parseError = true;
462 } else if (serviceLocation.isEmpty()) {
463 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_PATH;
464 parseError = true;
465 }
466 }
467
468 if (dupSTags[3] > 0)
469 serviceType = QService::InterProcess;
470
471 if ((dupSTags[2] > 0) && (dupSTags[3] > 0)) {
472 latestError = SFW_ERROR_MULTIPLE_SERVICE_TYPES;
473 parseError = true;
474 }
475
476 for (int i=0; !parseError && i<4; i++) {
477 if (dupSTags[i] > 1) {
478 latestError = SFW_ERROR_DUPLICATED_TAG;
479 parseError = true;
480 break;
481 }
482 }
483
484 //update all interfaces with service data
485 const int icount = serviceInterfaces.count();
486 if (icount == 0 && latestError == 0) {
487 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE;
488 parseError = true;
489 }
490 for (int i = 0; i<icount; i++) {
491 serviceInterfaces.at(i).d->serviceName = serviceName;
492 serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::Location] = serviceLocation;
493 serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::ServiceDescription] = serviceDescription;
494 serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::ServiceType] = serviceType;
495 }
496
497 return !parseError;
498}
499
500/*
501 Parses and extracts the interface metadata from the current xml <interface> node \n
502*/
503bool ServiceMetaData::processInterfaceElement(QXmlStreamReader &aXMLReader)
504{
505 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG));
506 bool parseError = false;
507
508 //Read interface parameter
509 QString tmp;
510 QServiceInterfaceDescriptor aInterface;
511 int dupITags[4] = {
512 0, //->iface name tag
513 0, //->version
514 0, //->capabilities
515 0 //->description
516 };
517 aInterface.d = new QServiceInterfaceDescriptorPrivate;
518
519 while (!parseError && !aXMLReader.atEnd()) {
520 aXMLReader.readNext();
521 //Read interface description
522 if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(NAME_TAG)) {
523 aInterface.d->interfaceName = aXMLReader.readElementText();
524 dupITags[0]++;
525 //Found <name> tag for interface
526 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(DESCRIPTION_TAG)) {
527 //Found <description> tag
528 aInterface.d->attributes[QServiceInterfaceDescriptor::InterfaceDescription] = aXMLReader.readElementText();
529 dupITags[3]++;
530 //Found </interface>, leave the loop
531 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_VERSION)) {
532 tmp.clear();
533 tmp = aXMLReader.readElementText();
534 if (tmp.isEmpty())
535 continue; //creates NO_INTERFACE_VERSION error further below
536 bool success = checkVersion(version: tmp);
537 if ( success ) {
538 int majorVer = -1;
539 int minorVer = -1;
540 transformVersion(version: tmp, major: &majorVer, minor: &minorVer);
541 aInterface.d->major = majorVer;
542 aInterface.d->minor = minorVer;
543 dupITags[1]++;
544 } else {
545 latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION;
546 parseError = true;
547 }
548 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_CAPABILITY)) {
549 tmp.clear();
550 tmp= aXMLReader.readElementText();
551 aInterface.d->attributes[QServiceInterfaceDescriptor::Capabilities] = tmp.split(sep: QLatin1String(","), behavior: QString::SkipEmptyParts);
552 dupITags[2]++;
553 } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_CUSTOM_PROPERTY)) {
554 parseError = true;
555 if (aXMLReader.attributes().hasAttribute(qualifiedName: QLatin1String("key"))) {
556 const QString ref = aXMLReader.attributes().value(qualifiedName: QLatin1String("key")).toString();
557 if (!ref.isEmpty()) {
558 if (aInterface.d->customAttributes.contains(akey: ref)) {
559 latestError = SFW_ERROR_DUPLICATED_CUSTOM_KEY;
560 continue;
561 } else {
562 QString value = aXMLReader.readElementText();
563 if (value.isNull())
564 value = QLatin1String("");
565 aInterface.d->customAttributes[ref] = value;
566 parseError = false;
567 }
568 }
569 }
570 if (parseError)
571 latestError = SFW_ERROR_INVALID_CUSTOM_TAG;
572 } else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)) {
573 break;
574 } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) {
575 latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE;
576 parseError = true;
577 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) {
578 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE;
579 parseError = true;
580 }
581 }
582
583 if (!parseError) {
584 if (dupITags[1] == 0) { //no version tag found
585 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION;
586 parseError = true;
587 } else if (aInterface.d->interfaceName.isEmpty()) {
588 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME;
589 parseError = true;
590 }
591 }
592
593 for (int i=0;!parseError && i<4;i++) {
594 if (dupITags[i] > 1) {
595 parseError = true;
596 latestError = SFW_ERROR_DUPLICATED_TAG;
597 break;
598 }
599 }
600
601 if (!parseError) {
602 const QString ident = aInterface.d->interfaceName
603 + QString::number(aInterface.majorVersion())
604 + QLatin1String(".")
605 + QString::number(aInterface.minorVersion());
606 if (duplicates.contains(value: ident.toLower())) {
607 latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE;
608 parseError = true;
609 } else {
610 duplicates.insert(value: ident.toLower());
611 serviceInterfaces.append(t: aInterface);
612 if (!m_latestIndex.contains(akey: aInterface.d->interfaceName.toLower())
613 || lessThan(d1: latestInterfaceVersion(interfaceName: aInterface.d->interfaceName), d2: aInterface))
614
615 {
616 m_latestIndex[aInterface.d->interfaceName.toLower()] = serviceInterfaces.count() - 1;
617 }
618 }
619 }
620 return !parseError;
621}
622
623QServiceInterfaceDescriptor ServiceMetaData::latestInterfaceVersion(const QString &interfaceName)
624{
625 QServiceInterfaceDescriptor ret;
626 if (m_latestIndex.contains(akey: interfaceName.toLower()))
627 return serviceInterfaces[m_latestIndex[interfaceName.toLower()]];
628 else
629 return ret;
630}
631
632QList<QServiceInterfaceDescriptor> ServiceMetaData::latestInterfaces() const
633{
634 QList<QServiceInterfaceDescriptor> interfaces;
635 QHash<QString,int>::const_iterator i = m_latestIndex.constBegin();
636 while (i != m_latestIndex.constEnd())
637 {
638 interfaces.append(t: serviceInterfaces[i.value()]);
639 ++i;
640 }
641 return interfaces;
642}
643
644bool ServiceMetaData::lessThan(const QServiceInterfaceDescriptor &d1,
645 const QServiceInterfaceDescriptor &d2) const
646{
647 return (d1.majorVersion() < d2.majorVersion())
648 || ( d1.majorVersion() == d2.majorVersion()
649 && d1.minorVersion() < d2.minorVersion());
650
651}
652
653bool ServiceMetaData::greaterThan(const QString &v1, const QString &v2) const
654{
655 int majorV1 = -1;
656 int minorV1 = -1;
657 transformVersion(version: v1, major: &majorV1, minor: &minorV1);
658
659 int majorV2 = -1;
660 int minorV2 = -1;
661 transformVersion(version: v2, major: &majorV2, minor: &minorV2);
662
663 return (majorV1 > majorV2
664 || (majorV1 == majorV2 && minorV1 > minorV2));
665}
666
667bool ServiceMetaData::checkVersion(const QString &version) const
668{
669 //match x.y as version format
670 QRegExp rx(QLatin1String("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"));
671 int pos = rx.indexIn(str: version);
672 QStringList list = rx.capturedTexts();
673 bool success = false;
674 if (pos == 0 && list.count() == 3
675 && rx.matchedLength() == version.length() )
676 {
677 list[1].toInt(ok: &success);
678 if ( success ) {
679 list[2].toInt(ok: &success);
680 }
681 }
682 return success;
683}
684
685void ServiceMetaData::transformVersion(const QString &version, int *major, int *minor) const
686{
687 Q_ASSERT(major != NULL);
688 Q_ASSERT(minor != NULL);
689 if (!checkVersion(version)) {
690 *major = -1;
691 *minor = -1;
692 } else {
693 QRegExp rx(QLatin1String("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"));
694 rx.indexIn(str: version);
695 QStringList list = rx.capturedTexts();
696 Q_ASSERT(list.count() == 3);
697 *major = list[1].toInt();
698 *minor = list[2].toInt();
699 }
700}
701
702 /*
703 * Clears the service metadata
704 *
705 */
706void ServiceMetaData::clearMetadata()
707{
708 xmlVersion = QLatin1String("1.0");
709 serviceName.clear();
710 serviceLocation.clear();
711 serviceDescription.clear();
712 serviceInterfaces.clear();
713 duplicates.clear();
714 m_latestIndex.clear();
715 serviceType = QService::Plugin;
716}
717
718QT_END_NAMESPACE
719

source code of qtsystems/src/serviceframework/servicemetadata.cpp