1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU Lesser General Public License as published by |
|
7 * the Free Software Foundation, version 2.1 of the License. |
|
8 * |
|
9 * This program is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 * GNU Lesser General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Lesser General Public License |
|
15 * along with this program. If not, |
|
16 * see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/". |
|
17 * |
|
18 * Description: |
|
19 * |
|
20 */ |
|
21 |
|
22 #include <QFile> |
|
23 #include "xqservicelog.h" |
|
24 #include "xqservicemetadata_p.h" |
|
25 #include <xqaiwinterfacedescriptor_p.h> |
|
26 |
|
27 //XML tags and attributes |
|
28 //General |
|
29 #define NAME_TAG "name" |
|
30 #define DESCRIPTION_TAG "description" |
|
31 |
|
32 //Service related |
|
33 #define SERVICE_TAG "service" |
|
34 #define SERVICE_FILEPATH "filepath" |
|
35 |
|
36 //Interface related |
|
37 #define INTERFACE_TAG "interface" |
|
38 #define INTERFACE_VERSION "version" |
|
39 #define INTERFACE_CAPABILITY "capabilities" |
|
40 #define INTERFACE_CUSTOM_PROPERTY "customproperty" |
|
41 |
|
42 QT_BEGIN_NAMESPACE |
|
43 |
|
44 #ifndef QT_NO_DATASTREAM |
|
45 QDataStream &operator<<(QDataStream &out, const ServiceMetaDataResults &r) |
|
46 { |
|
47 out << r.name << r.location; |
|
48 out << r.description << r.interfaces << r.latestInterfaces; |
|
49 |
|
50 return out; |
|
51 } |
|
52 |
|
53 QDataStream &operator>>(QDataStream &in, ServiceMetaDataResults &r) |
|
54 { |
|
55 in >> r.name >> r.location; |
|
56 in >> r.description >> r.interfaces >> r.latestInterfaces; |
|
57 |
|
58 return in; |
|
59 } |
|
60 #endif |
|
61 |
|
62 /*! |
|
63 \class ServiceMetaData |
|
64 \brief Utility class offering support for parsing metadata |
|
65 service xml registry files. |
|
66 |
|
67 Utility class (used by service database) that offers support for |
|
68 parsing metadata service xml registry file during service registration. \n |
|
69 |
|
70 It uses QXMLStreamReader class for parsing. Supproted Operations are: |
|
71 - Parse the service and interfaces defined in XML file |
|
72 - name, version, capabilitiesList, description and filePath of service can be retrieved |
|
73 - each interface can be retrieved |
|
74 */ |
|
75 |
|
76 /*! |
|
77 Constructor |
|
78 \param aXmlFilePath Path to the xml file that describes the service. |
|
79 */ |
|
80 ServiceMetaData::ServiceMetaData(const QString &aXmlFilePath) |
|
81 { |
|
82 XQSERVICE_DEBUG_PRINT("ServiceMetaData::ServiceMetaData(1)"); |
|
83 XQSERVICE_DEBUG_PRINT("aXmlFilePath: %s", qPrintable(aXmlFilePath)); |
|
84 xmlDevice = new QFile(aXmlFilePath); |
|
85 ownsXmlDevice = true; |
|
86 latestError = 0; |
|
87 } |
|
88 |
|
89 /*! |
|
90 Constructor |
|
91 \param device QIODevice that contains the XML data that describes the service. |
|
92 */ |
|
93 ServiceMetaData::ServiceMetaData(QIODevice *device) |
|
94 { |
|
95 XQSERVICE_DEBUG_PRINT("ServiceMetaData::ServiceMetaData(2)"); |
|
96 xmlDevice = device; |
|
97 ownsXmlDevice = false; |
|
98 latestError = 0; |
|
99 } |
|
100 |
|
101 /*! |
|
102 Destructor |
|
103 */ |
|
104 ServiceMetaData::~ServiceMetaData() |
|
105 { |
|
106 XQSERVICE_DEBUG_PRINT("ServiceMetaData::~ServiceMetaData"); |
|
107 if (ownsXmlDevice) |
|
108 delete xmlDevice; |
|
109 } |
|
110 |
|
111 /*! |
|
112 Sets the device containing the XML data that describes the service to \a device. |
|
113 \param device Device containing XML data. |
|
114 */ |
|
115 void ServiceMetaData::setDevice(QIODevice *device) |
|
116 { |
|
117 XQSERVICE_DEBUG_PRINT("ServiceMetaData::setDevice"); |
|
118 clearMetadata(); |
|
119 xmlDevice = device; |
|
120 ownsXmlDevice = false; |
|
121 } |
|
122 |
|
123 /*! |
|
124 Gets the device containing the XML data that describes the service. |
|
125 \return Device containing the XML data. |
|
126 */ |
|
127 QIODevice *ServiceMetaData::device() const |
|
128 { |
|
129 XQSERVICE_DEBUG_PRINT("ServiceMetaData::device"); |
|
130 return xmlDevice; |
|
131 } |
|
132 |
|
133 /*! |
|
134 Gets the service name. |
|
135 \return Service name or default value (empty string) if it is not available. |
|
136 */ |
|
137 |
|
138 /*QString ServiceMetaData::name() const |
|
139 { |
|
140 return serviceName; |
|
141 }*/ |
|
142 |
|
143 /*! |
|
144 Gets the path of the service implementation file. |
|
145 \return Service implementation filepath. |
|
146 */ |
|
147 /*QString ServiceMetaData::location() const |
|
148 { |
|
149 return serviceLocation; |
|
150 }*/ |
|
151 |
|
152 /*! |
|
153 Gets the service description. |
|
154 \return Service description or default value (empty string) if it is not available. |
|
155 */ |
|
156 /*QString ServiceMetaData::description() const |
|
157 { |
|
158 return serviceDescription; |
|
159 }*/ |
|
160 |
|
161 /*! |
|
162 Gets the list of interfaces. |
|
163 \return List interfaces. |
|
164 */ |
|
165 /*QList<XQServiceInterfaceDescriptor> ServiceMetaData::getInterfaces() const |
|
166 { |
|
167 return serviceInterfaces; |
|
168 } */ |
|
169 |
|
170 /*! |
|
171 \internal |
|
172 Returns a streamable object containing the results of the parsing. |
|
173 */ |
|
174 ServiceMetaDataResults ServiceMetaData::parseResults() const |
|
175 { |
|
176 XQSERVICE_DEBUG_PRINT("ServiceMetaData::parseResults"); |
|
177 |
|
178 ServiceMetaDataResults results; |
|
179 results.location = serviceLocation; |
|
180 results.name = serviceName; |
|
181 results.description = serviceDescription; |
|
182 results.interfaces = serviceInterfaces; |
|
183 results.latestInterfaces = latestInterfaces(); |
|
184 results.version = version; |
|
185 |
|
186 return results; |
|
187 } |
|
188 |
|
189 /*! |
|
190 Parses the file and extracts the service metadata \n |
|
191 Custom error codes: \n |
|
192 SFW_ERROR_UNABLE_TO_OPEN_FILE in case can not open the XML file \n |
|
193 SFW_ERROR_INVALID_XML_FILE in case service registry is not a valid XML file \n |
|
194 SFW_ERROR_NO_SERVICE in case XML file has no service tag\n |
|
195 \return true if the metadata was read properly, false if there is an error. |
|
196 */ |
|
197 bool ServiceMetaData::extractMetadata() |
|
198 { |
|
199 XQSERVICE_DEBUG_PRINT("ServiceMetaData::extractMetadata"); |
|
200 latestError = 0; |
|
201 clearMetadata(); |
|
202 version = ServiceMetaDataResults::VERSION_2; // default |
|
203 |
|
204 QXmlStreamReader xmlReader; |
|
205 bool parseError = false; |
|
206 //Open xml file |
|
207 if (!xmlDevice->isOpen() && !xmlDevice->open(QIODevice::ReadOnly)) { |
|
208 XQSERVICE_DEBUG_PRINT("XML error:Couldn't open the file"); |
|
209 latestError = ServiceMetaData::SFW_ERROR_UNABLE_TO_OPEN_FILE; |
|
210 parseError = true; |
|
211 } else { |
|
212 //Load xml content |
|
213 xmlReader.setDevice(xmlDevice); |
|
214 // Read XML doc |
|
215 while (!xmlReader.atEnd() && !parseError) { |
|
216 xmlReader.readNext(); |
|
217 //Found a <service> node, read service related metadata |
|
218 if (xmlReader.isStartElement() && xmlReader.name() == SERVICE_TAG) { |
|
219 |
|
220 // Support for previous XML version. Check if service element has name attribute |
|
221 // If so, assume the old element |
|
222 if (getAttributeValue(xmlReader, NAME_TAG, serviceName)) { |
|
223 if (!processServiceElementPrevVersion(xmlReader)) { |
|
224 XQSERVICE_DEBUG_PRINT("XML error: Couldn't process service element"); |
|
225 parseError = true; |
|
226 } |
|
227 } |
|
228 else if (!processServiceElement(xmlReader)) { |
|
229 XQSERVICE_DEBUG_PRINT("XML error: Couldn't process service element"); |
|
230 parseError = true; |
|
231 } |
|
232 } |
|
233 else if (xmlReader.isStartElement() && xmlReader.name() != SERVICE_TAG) { |
|
234 XQSERVICE_DEBUG_PRINT("XML error: No service"); |
|
235 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; |
|
236 parseError = true; |
|
237 } |
|
238 else if (xmlReader.tokenType() == QXmlStreamReader::Invalid) { |
|
239 XQSERVICE_DEBUG_PRINT("XML error: Invalid XML"); |
|
240 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
241 parseError = true; |
|
242 } |
|
243 } |
|
244 if (ownsXmlDevice) |
|
245 xmlDevice->close(); |
|
246 } |
|
247 if (parseError) { |
|
248 { |
|
249 XQSERVICE_DEBUG_PRINT("XML Parse error, line=%d,column=%d", xmlReader.lineNumber(), xmlReader.columnNumber()); |
|
250 clearMetadata(); |
|
251 } |
|
252 } |
|
253 XQSERVICE_DEBUG_PRINT("XML parseError: %d", parseError); |
|
254 return !parseError; |
|
255 } |
|
256 |
|
257 /*! |
|
258 Gets the latest parsing error. |
|
259 \return Parsing error(negative value) or 0 in case there is none. |
|
260 */ |
|
261 int ServiceMetaData::getLatestError() const |
|
262 { |
|
263 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getLatestError"); |
|
264 XQSERVICE_DEBUG_PRINT("latestError: %d", latestError); |
|
265 return latestError; |
|
266 } |
|
267 |
|
268 /*! |
|
269 Parses and extracts the service from the current xml <service> node |
|
270 using the new format (Version 2). |
|
271 Schema: |
|
272 <!ELEMENT service ( name, filepath, description?, interface+ ) > |
|
273 <!ELEMENT description ( #CDATA ) > |
|
274 <!ELEMENT filepath ( #PCDATA ) > |
|
275 <!ELEMENT interface ( name, version, description?, capabilities?, customproperty* ) > |
|
276 <!ELEMENT capabilities ( #PCDATA ) > |
|
277 <!ELEMENT name ( #PCDATA ) > |
|
278 <!ELEMENT version ( #PCDATA ) > |
|
279 <!ELEMENT customproperty ( #CDATA ) > |
|
280 <!ATTLIST customproperty key NMTOKEN #REQUIRED > |
|
281 */ |
|
282 bool ServiceMetaData::processServiceElement(QXmlStreamReader &aXMLReader) |
|
283 { |
|
284 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processServiceElement"); |
|
285 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_TAG); |
|
286 bool parseError = false; |
|
287 |
|
288 int dupSTags[3] = {0 //->tag name |
|
289 ,0 //-> service description |
|
290 ,0 //-> filepath |
|
291 }; |
|
292 while(!parseError && !aXMLReader.atEnd()) { |
|
293 aXMLReader.readNext(); |
|
294 if (aXMLReader.isStartElement() && aXMLReader.name() == DESCRIPTION_TAG) { |
|
295 //Found <description> tag |
|
296 serviceDescription = aXMLReader.readElementText(); |
|
297 dupSTags[1]++; |
|
298 } else if (aXMLReader.isStartElement() && aXMLReader.name() == NAME_TAG) { |
|
299 serviceName = aXMLReader.readElementText(); |
|
300 dupSTags[0]++; |
|
301 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
302 //Found a <interface> node, read module related metadata |
|
303 if (!processInterfaceElement(aXMLReader)) |
|
304 parseError = true; |
|
305 } else if (aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_FILEPATH ) { |
|
306 //Found <filepath> tag |
|
307 dupSTags[2]++; |
|
308 serviceLocation = aXMLReader.readElementText(); |
|
309 } else if (aXMLReader.isStartElement() && aXMLReader.name() == "version") { |
|
310 //FOUND <version> tag on service level. We ignore this for now |
|
311 aXMLReader.readElementText(); |
|
312 } else if (aXMLReader.isEndElement() && aXMLReader.name() == SERVICE_TAG) { |
|
313 //Found </service>, leave the loop |
|
314 break; |
|
315 } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { |
|
316 latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; |
|
317 parseError = true; |
|
318 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
319 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
320 parseError = true; |
|
321 } |
|
322 } |
|
323 if ( !parseError ) { |
|
324 if (serviceName.isEmpty()) { |
|
325 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; |
|
326 parseError = true; |
|
327 } else if (serviceLocation.isEmpty()) { |
|
328 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_FILEPATH; |
|
329 parseError = true; |
|
330 } |
|
331 } |
|
332 |
|
333 for(int i=0;!parseError && i<3;i++) { |
|
334 if (dupSTags[i] > 1) { |
|
335 parseError = true; |
|
336 latestError = SFW_ERROR_DUPLICATED_TAG; |
|
337 break; |
|
338 } |
|
339 } |
|
340 |
|
341 //update all interfaces with service data |
|
342 const int icount = serviceInterfaces.count(); |
|
343 if (icount == 0 && latestError == 0) { |
|
344 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; |
|
345 parseError = true; |
|
346 } |
|
347 for (int i = 0; i<icount; i++) { |
|
348 serviceInterfaces.at(i).d->serviceName = serviceName; |
|
349 serviceInterfaces.at(i).d->properties[XQAiwInterfaceDescriptor::Location] = serviceLocation; |
|
350 serviceInterfaces.at(i).d->properties[XQAiwInterfaceDescriptor::ServiceDescription] = serviceDescription; |
|
351 } |
|
352 |
|
353 if (parseError) { |
|
354 clearMetadata(); |
|
355 } |
|
356 XQSERVICE_DEBUG_PRINT("processServiceElement parseError: %d", parseError); |
|
357 return !parseError; |
|
358 } |
|
359 |
|
360 /*! |
|
361 Parses and extracts the service from the current xml <service> node |
|
362 using the new format (Version 1) \n |
|
363 |
|
364 <!ELEMENT service ( description?, interface+ ) > |
|
365 <!ATTLIST service name #CDATA #REQUIRED > |
|
366 <!ATTLIST service filepath #CDATA #REQUIRED > |
|
367 <!ELEMENT description ( #CDATA ) > |
|
368 <!ELEMENT interface ( description? ) > |
|
369 <!ATTLIST interface name #CDATA #REQUIRED > |
|
370 <!ATTLIST interface version #CDATA #REQUIRED > |
|
371 <!ATTLIST interface capabilities #CDATA > |
|
372 |
|
373 Custom error codes: \n |
|
374 SFW_ERROR_NO_SERVICE_NAME in case no service name in XML file \n |
|
375 SFW_ERROR_NO_INTERFACE_VERSION in case no interface version in XML file \n |
|
376 SFW_ERROR_PARSE_SERVICE in case can not parse service section in XML file \n |
|
377 SFW_ERROR_NO_SERVICE_FILEPATH in case no service file path in XML file \n |
|
378 SFW_ERROR_INVALID_XML_FILE in case XML file is not valid \n |
|
379 SFW_ERROR_NO_SERVICE_INTERFACE in case no interface defined for service in XML file. |
|
380 \param aXMLReader xml stream reader . |
|
381 \return true if the metadata was read properly, false if there is an error. |
|
382 |
|
383 |
|
384 */ |
|
385 bool ServiceMetaData::processServiceElementPrevVersion(QXmlStreamReader &aXMLReader) |
|
386 { |
|
387 version = ServiceMetaDataResults::VERSION_1; // Previous version |
|
388 |
|
389 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processServiceElementPrevVersion"); |
|
390 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_TAG); |
|
391 bool parseError = false; |
|
392 |
|
393 QString tmp; |
|
394 if (!getAttributeValue(aXMLReader, NAME_TAG, tmp)) { |
|
395 XQSERVICE_DEBUG_PRINT("No service name"); |
|
396 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; |
|
397 parseError = true; |
|
398 } |
|
399 |
|
400 if (!parseError) { |
|
401 if (!getAttributeValue(aXMLReader, SERVICE_FILEPATH, serviceLocation)) { |
|
402 XQSERVICE_DEBUG_PRINT("No service filepath"); |
|
403 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_FILEPATH; |
|
404 parseError = true; |
|
405 } |
|
406 } |
|
407 |
|
408 while (!parseError && !aXMLReader.atEnd()) { |
|
409 aXMLReader.readNext(); |
|
410 if (aXMLReader.name() == DESCRIPTION_TAG) { |
|
411 serviceDescription = aXMLReader.readElementText(); |
|
412 XQSERVICE_DEBUG_PRINT("serviceDescription: %s", qPrintable(serviceDescription)); |
|
413 //Found a <interface> node, read module related metadata |
|
414 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
415 if (!processInterfaceElementPrevVersion(aXMLReader)){ |
|
416 XQSERVICE_DEBUG_PRINT("Couldn't process interface element"); |
|
417 parseError = true; |
|
418 } |
|
419 //Found </service>, leave the loop |
|
420 } else if (aXMLReader.isEndElement() && aXMLReader.name() == SERVICE_TAG) { |
|
421 XQSERVICE_DEBUG_PRINT("Service element handled"); |
|
422 break; |
|
423 } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { |
|
424 XQSERVICE_DEBUG_PRINT("Service parse error"); |
|
425 latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; |
|
426 parseError = true; |
|
427 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
428 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
429 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
430 parseError = true; |
|
431 } |
|
432 } |
|
433 |
|
434 if (serviceInterfaces.count() == 0 && latestError == 0) { |
|
435 XQSERVICE_DEBUG_PRINT("No service interface"); |
|
436 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; |
|
437 parseError = true; |
|
438 } |
|
439 if (parseError) { |
|
440 clearMetadata(); |
|
441 } |
|
442 XQSERVICE_DEBUG_PRINT("parseError: %d", parseError); |
|
443 return !parseError; |
|
444 } |
|
445 |
|
446 |
|
447 /*! |
|
448 Parses and extracts the interface metadata from the current xml <interface> node. |
|
449 \param aXMLReader xml stream reader . |
|
450 \return true if the metadata was read properly, false if there is an error. |
|
451 */ |
|
452 bool ServiceMetaData::processInterfaceElement(QXmlStreamReader &aXMLReader) |
|
453 { |
|
454 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processInterfaceElement"); |
|
455 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG); |
|
456 bool parseError = false; |
|
457 |
|
458 //Read interface parameter |
|
459 QString tmp; |
|
460 XQAiwInterfaceDescriptor aInterface; |
|
461 int dupITags[4] = { |
|
462 0, //->iface name tag |
|
463 0, //->version |
|
464 0, //->capabilities |
|
465 0 //->description |
|
466 }; |
|
467 aInterface.d = new XQAiwInterfaceDescriptorPrivate; |
|
468 while (!parseError && !aXMLReader.atEnd()) { |
|
469 aXMLReader.readNext(); |
|
470 //Read interface description |
|
471 if (aXMLReader.isStartElement() && aXMLReader.name() == NAME_TAG) { |
|
472 aInterface.d->interfaceName = aXMLReader.readElementText(); |
|
473 dupITags[0]++; |
|
474 //Found <name> tag for interface |
|
475 } else if (aXMLReader.isStartElement() && aXMLReader.name() == DESCRIPTION_TAG) { |
|
476 //Found <description> tag |
|
477 aInterface.d->properties[XQAiwInterfaceDescriptor::InterfaceDescription] = aXMLReader.readElementText(); |
|
478 dupITags[3]++; |
|
479 //Found </interface>, leave the loop |
|
480 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_VERSION) { |
|
481 tmp.clear(); |
|
482 tmp = aXMLReader.readElementText(); |
|
483 if (tmp.isEmpty()) |
|
484 continue; //creates NO_INTERFACE_VERSION error further below |
|
485 bool success = checkVersion(tmp); |
|
486 if ( success ) { |
|
487 int majorVer = -1; |
|
488 int minorVer = -1; |
|
489 transformVersion(tmp, &majorVer, &minorVer); |
|
490 aInterface.d->major = majorVer; |
|
491 aInterface.d->minor = minorVer; |
|
492 dupITags[1]++; |
|
493 } else { |
|
494 latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION; |
|
495 parseError = true; |
|
496 } |
|
497 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_CAPABILITY) { |
|
498 tmp.clear(); |
|
499 tmp= aXMLReader.readElementText(); |
|
500 aInterface.d->properties[XQAiwInterfaceDescriptor::Capabilities] = tmp.split(",", QString::SkipEmptyParts); |
|
501 dupITags[2]++; |
|
502 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_CUSTOM_PROPERTY) { |
|
503 parseError = true; |
|
504 if (aXMLReader.attributes().hasAttribute("key")) { |
|
505 const QString ref = aXMLReader.attributes().value("key").toString(); |
|
506 XQSERVICE_DEBUG_PRINT("Custom property key: %s", qPrintable(ref)); |
|
507 if (!ref.isEmpty()) { |
|
508 if (aInterface.d->customProperties.contains(ref)) { |
|
509 latestError = SFW_ERROR_DUPLICATED_CUSTOM_KEY; |
|
510 continue; |
|
511 } else { |
|
512 QString value = aXMLReader.readElementText(); |
|
513 if (value.isEmpty() || value.isNull()) |
|
514 value = QString(""); |
|
515 XQSERVICE_DEBUG_PRINT("Custom property value: %s", qPrintable(value)); |
|
516 aInterface.d->customProperties[ref] = value; |
|
517 parseError = false; |
|
518 } |
|
519 } |
|
520 } |
|
521 if (parseError) |
|
522 latestError = SFW_ERROR_INVALID_CUSTOM_TAG; |
|
523 } else if (aXMLReader.isEndElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
524 break; |
|
525 } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) { |
|
526 latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE; |
|
527 parseError = true; |
|
528 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
529 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
530 parseError = true; |
|
531 } |
|
532 } |
|
533 |
|
534 if (!parseError) { |
|
535 if (dupITags[1] == 0) { //no version tag found |
|
536 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; |
|
537 parseError = true; |
|
538 } else if (aInterface.d->interfaceName.isEmpty()) { |
|
539 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; |
|
540 parseError = true; |
|
541 } |
|
542 } |
|
543 |
|
544 for(int i=0;!parseError && i<4;i++) { |
|
545 if (dupITags[i] > 1) { |
|
546 parseError = true; |
|
547 latestError = SFW_ERROR_DUPLICATED_TAG; |
|
548 break; |
|
549 } |
|
550 } |
|
551 |
|
552 if (!parseError) { |
|
553 const QString ident = aInterface.d->interfaceName |
|
554 + QString::number(aInterface.majorVersion()) |
|
555 + "." |
|
556 + QString::number(aInterface.minorVersion()); |
|
557 if (duplicates.contains(ident.toLower())) { |
|
558 latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE; |
|
559 parseError = true; |
|
560 } else { |
|
561 duplicates.insert(ident.toLower()); |
|
562 serviceInterfaces.append(aInterface); |
|
563 if (!m_latestIndex.contains(aInterface.d->interfaceName.toLower()) |
|
564 || lessThan(latestInterfaceVersion(aInterface.d->interfaceName), aInterface)) |
|
565 |
|
566 { |
|
567 m_latestIndex[aInterface.d->interfaceName.toLower()] = serviceInterfaces.count() - 1; |
|
568 } |
|
569 } |
|
570 } |
|
571 |
|
572 if (parseError) |
|
573 { |
|
574 // Delete garbage |
|
575 delete aInterface.d; |
|
576 aInterface.d = 0; |
|
577 } |
|
578 XQSERVICE_DEBUG_PRINT("processInterfaceElement parseError: %d", parseError); |
|
579 return !parseError; |
|
580 } |
|
581 |
|
582 /*! |
|
583 Parses and extracts the interface metadata from the current xml <interface> node. \n |
|
584 Custome error codes: \n |
|
585 SFW_ERROR_NO_INTERFACE_NAME in case no interface name in XML file \n |
|
586 SFW_ERROR_PARSE_INTERFACE in case error parsing interface section \n |
|
587 SFW_ERROR_INVALID_XML_FILE in case XML file is not valid \n |
|
588 \param aXMLReader xml stream reader. |
|
589 \return true if the metadata was read properly, false if there is an error. |
|
590 */ |
|
591 bool ServiceMetaData::processInterfaceElementPrevVersion(QXmlStreamReader &aXMLReader) |
|
592 { |
|
593 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processInterfaceElementPrevVersion"); |
|
594 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG); |
|
595 bool parseError = false; |
|
596 |
|
597 //Read interface parameter |
|
598 QString tmp; |
|
599 XQAiwInterfaceDescriptor aInterface; |
|
600 aInterface.d = new XQAiwInterfaceDescriptorPrivate; |
|
601 aInterface.d->serviceName = serviceName; // picked earlier !!! |
|
602 XQSERVICE_DEBUG_PRINT("Service name %s", qPrintable(serviceName)); |
|
603 |
|
604 int dupITags[4] = { |
|
605 0, //->iface name tag |
|
606 0, //->version |
|
607 0, //->capabilities |
|
608 0 //->description |
|
609 }; |
|
610 |
|
611 if (getAttributeValue(aXMLReader, NAME_TAG, tmp)) { |
|
612 XQSERVICE_DEBUG_PRINT("Name attribute value"); |
|
613 aInterface.d->interfaceName = tmp; |
|
614 tmp.clear(); |
|
615 if (getAttributeValue(aXMLReader, INTERFACE_VERSION, tmp)) { |
|
616 XQSERVICE_DEBUG_PRINT("Interface version value"); |
|
617 bool success = checkVersion(tmp); |
|
618 if ( success ) { |
|
619 int majorVer = -1; |
|
620 int minorVer = -1; |
|
621 transformVersion(tmp, &majorVer, &minorVer); |
|
622 aInterface.d->major = majorVer; |
|
623 aInterface.d->minor = minorVer; |
|
624 dupITags[1]++; |
|
625 |
|
626 tmp.clear(); // Rememember to clear |
|
627 if (getAttributeValue(aXMLReader, INTERFACE_CAPABILITY, tmp)) { |
|
628 XQSERVICE_DEBUG_PRINT("Interface capability value"); |
|
629 aInterface.d->properties[XQAiwInterfaceDescriptor::Capabilities] = tmp.split(",", QString::SkipEmptyParts); |
|
630 } |
|
631 } else { |
|
632 XQSERVICE_DEBUG_PRINT("Invalid interface version"); |
|
633 latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION; |
|
634 parseError = true; |
|
635 } |
|
636 } |
|
637 else{ |
|
638 XQSERVICE_DEBUG_PRINT("No interface version"); |
|
639 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; |
|
640 parseError = true; |
|
641 } |
|
642 } else { |
|
643 XQSERVICE_DEBUG_PRINT("No interface name"); |
|
644 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; |
|
645 parseError = true; |
|
646 } |
|
647 |
|
648 while (!parseError && !aXMLReader.atEnd()) { |
|
649 aXMLReader.readNext(); |
|
650 //Read interface description |
|
651 if (aXMLReader.isStartElement() && aXMLReader.name() == DESCRIPTION_TAG) { |
|
652 XQSERVICE_DEBUG_PRINT("Interface description"); |
|
653 aInterface.d->properties[XQAiwInterfaceDescriptor::InterfaceDescription] = aXMLReader.readElementText(); |
|
654 //Found </interface>, leave the loop |
|
655 } else if (aXMLReader.isEndElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
656 XQSERVICE_DEBUG_PRINT("Interface handled"); |
|
657 break; |
|
658 } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) { |
|
659 XQSERVICE_DEBUG_PRINT("Interface parse error"); |
|
660 latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE; |
|
661 parseError = true; |
|
662 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
663 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
664 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
665 parseError = true; |
|
666 } |
|
667 } |
|
668 |
|
669 // Consistency check |
|
670 if (!parseError) { |
|
671 if (dupITags[1] == 0) { //no version tag found |
|
672 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; |
|
673 parseError = true; |
|
674 } else if (aInterface.d->interfaceName.isEmpty()) { |
|
675 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; |
|
676 parseError = true; |
|
677 } |
|
678 } |
|
679 |
|
680 if (!parseError) { |
|
681 const QString ident = aInterface.d->interfaceName |
|
682 + QString::number(aInterface.majorVersion()) |
|
683 + "." |
|
684 + QString::number(aInterface.minorVersion()); |
|
685 XQSERVICE_DEBUG_PRINT("ident: %s", qPrintable(ident)); |
|
686 if (duplicates.contains(ident.toLower())) { |
|
687 XQSERVICE_DEBUG_PRINT("Duplicate interface"); |
|
688 latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE; |
|
689 parseError = true; |
|
690 } else { |
|
691 duplicates.insert(ident.toLower()); |
|
692 serviceInterfaces.append(aInterface); |
|
693 if (!m_latestIndex.contains(aInterface.d->interfaceName.toLower()) |
|
694 || lessThan(latestInterfaceVersion(aInterface.d->interfaceName), aInterface)) |
|
695 |
|
696 { |
|
697 m_latestIndex[aInterface.d->interfaceName.toLower()] = serviceInterfaces.count() - 1; |
|
698 } |
|
699 } |
|
700 } |
|
701 |
|
702 if (parseError) |
|
703 { |
|
704 // Delete garbage |
|
705 delete aInterface.d; |
|
706 aInterface.d = 0; |
|
707 } |
|
708 |
|
709 XQSERVICE_DEBUG_PRINT("processInterfaceElementPrevVersion parseError: %d", parseError); |
|
710 return !parseError; |
|
711 } |
|
712 |
|
713 |
|
714 /*! |
|
715 Gets the value of the attribute from the XML node. |
|
716 \param aDomElement Xml node. |
|
717 \param aAttributeName Attribute name. |
|
718 \param aValue [out] attribute value. |
|
719 \return true if the value was read, false otherwise. |
|
720 */ |
|
721 bool ServiceMetaData::getAttributeValue(const QXmlStreamReader &aXMLReader, const QString &aAttributeName, QString &aValue) |
|
722 { |
|
723 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getAttributeValue"); |
|
724 XQSERVICE_DEBUG_PRINT("aAttributeName: %s", qPrintable(aAttributeName)); |
|
725 bool result = false; |
|
726 for (int i = 0; i < aXMLReader.attributes().count(); i++){ |
|
727 QXmlStreamAttribute att = aXMLReader.attributes()[i]; |
|
728 if (att.name() == aAttributeName) { |
|
729 if (att.value().isNull() || att.value().isEmpty()) { |
|
730 result = false; |
|
731 } else { |
|
732 result = true; |
|
733 aValue = att.value().toString(); |
|
734 XQSERVICE_DEBUG_PRINT("aValue: %s", qPrintable(aValue)); |
|
735 } |
|
736 } |
|
737 } |
|
738 // Capability attribute is allowed to be empty |
|
739 if (aAttributeName == INTERFACE_CAPABILITY) { |
|
740 result = true; |
|
741 } |
|
742 XQSERVICE_DEBUG_PRINT("result: %d", result); |
|
743 return result; |
|
744 } |
|
745 |
|
746 |
|
747 XQAiwInterfaceDescriptor ServiceMetaData::latestInterfaceVersion(const QString &interfaceName) |
|
748 { |
|
749 XQAiwInterfaceDescriptor ret; |
|
750 if (m_latestIndex.contains(interfaceName.toLower())) |
|
751 return serviceInterfaces[m_latestIndex[interfaceName.toLower()]]; |
|
752 else |
|
753 return ret; |
|
754 } |
|
755 |
|
756 QList<XQAiwInterfaceDescriptor> ServiceMetaData::latestInterfaces() const |
|
757 { |
|
758 XQSERVICE_DEBUG_PRINT("ServiceMetaData::latestInterfaces"); |
|
759 QList<XQAiwInterfaceDescriptor> interfaces; |
|
760 QHash<QString,int>::const_iterator i = m_latestIndex.constBegin(); |
|
761 while(i != m_latestIndex.constEnd()) |
|
762 { |
|
763 interfaces.append(serviceInterfaces[i.value()]); |
|
764 ++i; |
|
765 } |
|
766 return interfaces; |
|
767 } |
|
768 |
|
769 bool ServiceMetaData::lessThan(const XQAiwInterfaceDescriptor &d1, |
|
770 const XQAiwInterfaceDescriptor &d2) const |
|
771 { |
|
772 return (d1.majorVersion() < d2.majorVersion()) |
|
773 || ( d1.majorVersion() == d2.majorVersion() |
|
774 && d1.minorVersion() < d2.minorVersion()); |
|
775 |
|
776 } |
|
777 |
|
778 bool ServiceMetaData::checkVersion(const QString &version) const |
|
779 { |
|
780 XQSERVICE_DEBUG_PRINT("ServiceMetaData::checkVersion"); |
|
781 //match x.y as version format |
|
782 QRegExp rx("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"); |
|
783 int pos = rx.indexIn(version); |
|
784 QStringList list = rx.capturedTexts(); |
|
785 bool success = false; |
|
786 if (pos == 0 && list.count() == 3 |
|
787 && rx.matchedLength() == version.length() ) |
|
788 { |
|
789 list[1].toInt(&success); |
|
790 if ( success ) { |
|
791 list[2].toInt(&success); |
|
792 } |
|
793 } |
|
794 XQSERVICE_DEBUG_PRINT("success: %d", success); |
|
795 return success; |
|
796 } |
|
797 |
|
798 void ServiceMetaData::transformVersion(const QString &version, int *major, int *minor) const |
|
799 { |
|
800 Q_ASSERT(major != NULL); |
|
801 Q_ASSERT(minor != NULL); |
|
802 if(!checkVersion(version)) { |
|
803 *major = -1; |
|
804 *minor = -1; |
|
805 } else { |
|
806 QRegExp rx("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"); |
|
807 rx.indexIn(version); |
|
808 QStringList list = rx.capturedTexts(); |
|
809 Q_ASSERT(list.count() == 3); |
|
810 *major = list[1].toInt(); |
|
811 *minor = list[2].toInt(); |
|
812 } |
|
813 } |
|
814 |
|
815 /*! |
|
816 Clears the service metadata. |
|
817 */ |
|
818 void ServiceMetaData::clearMetadata() |
|
819 { |
|
820 XQSERVICE_DEBUG_PRINT("ServiceMetaData::clearMetadata"); |
|
821 serviceName.clear(); |
|
822 serviceLocation.clear(); |
|
823 serviceDescription.clear(); |
|
824 serviceInterfaces.clear(); |
|
825 duplicates.clear(); |
|
826 m_latestIndex.clear(); |
|
827 version = 0; |
|
828 } |
|
829 |
|
830 |
|
831 |
|
832 QT_END_NAMESPACE |
|