|
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 |
|
65 Utility class (used by service database) that offers support for |
|
66 parsing metadata service xml registry file during service registration. \n |
|
67 |
|
68 It uses QXMLStreamReader class for parsing. Supproted Operations are: |
|
69 - Parse the service and interfaces defined in XML file |
|
70 - name, version, capabilitiesList, description and filePath of service can be retrieved |
|
71 - each interface can be retrieved |
|
72 */ |
|
73 |
|
74 /* |
|
75 * Class constructor |
|
76 * |
|
77 * @param aXmlFilePath path to the xml file that describes the service. |
|
78 */ |
|
79 ServiceMetaData::ServiceMetaData(const QString &aXmlFilePath) |
|
80 { |
|
81 XQSERVICE_DEBUG_PRINT("ServiceMetaData::ServiceMetaData(1)"); |
|
82 XQSERVICE_DEBUG_PRINT("aXmlFilePath: %s", qPrintable(aXmlFilePath)); |
|
83 xmlDevice = new QFile(aXmlFilePath); |
|
84 ownsXmlDevice = true; |
|
85 latestError = 0; |
|
86 } |
|
87 |
|
88 /* |
|
89 * Class constructor |
|
90 * |
|
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 * Class destructor |
|
103 * |
|
104 */ |
|
105 ServiceMetaData::~ServiceMetaData() |
|
106 { |
|
107 XQSERVICE_DEBUG_PRINT("ServiceMetaData::~ServiceMetaData"); |
|
108 if (ownsXmlDevice) |
|
109 delete xmlDevice; |
|
110 } |
|
111 |
|
112 /* |
|
113 Sets the device containing the XML data that describes the service to \a device. |
|
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 Returns the device containing the XML data that describes the service. |
|
125 */ |
|
126 QIODevice *ServiceMetaData::device() const |
|
127 { |
|
128 XQSERVICE_DEBUG_PRINT("ServiceMetaData::device"); |
|
129 return xmlDevice; |
|
130 } |
|
131 |
|
132 /* |
|
133 * Gets the service name |
|
134 * |
|
135 * @return service name or default value (empty string) if it is not available |
|
136 */ |
|
137 /*QString ServiceMetaData::name() const |
|
138 { |
|
139 return serviceName; |
|
140 }*/ |
|
141 |
|
142 /* |
|
143 * Gets the path of the service implementation file |
|
144 * |
|
145 * @return service implementation filepath |
|
146 */ |
|
147 /*QString ServiceMetaData::location() const |
|
148 { |
|
149 return serviceLocation; |
|
150 }*/ |
|
151 |
|
152 /* |
|
153 * Gets the service description |
|
154 * |
|
155 * @return service description or default value (empty string) if it is not available |
|
156 */ |
|
157 /*QString ServiceMetaData::description() const |
|
158 { |
|
159 return serviceDescription; |
|
160 }*/ |
|
161 |
|
162 /* |
|
163 Returns the metadata of the interace at \a index; otherwise |
|
164 returns 0. |
|
165 */ |
|
166 /*QList<XQServiceInterfaceDescriptor> ServiceMetaData::getInterfaces() const |
|
167 { |
|
168 return serviceInterfaces; |
|
169 } */ |
|
170 |
|
171 /*! |
|
172 \internal |
|
173 |
|
174 Returns a streamable object containing the results of the parsing. |
|
175 */ |
|
176 ServiceMetaDataResults ServiceMetaData::parseResults() const |
|
177 { |
|
178 XQSERVICE_DEBUG_PRINT("ServiceMetaData::parseResults"); |
|
179 |
|
180 ServiceMetaDataResults results; |
|
181 results.location = serviceLocation; |
|
182 results.name = serviceName; |
|
183 results.description = serviceDescription; |
|
184 results.interfaces = serviceInterfaces; |
|
185 results.latestInterfaces = latestInterfaces(); |
|
186 results.version = version; |
|
187 |
|
188 return results; |
|
189 } |
|
190 |
|
191 /* |
|
192 Parses the file and extracts the service metadata \n |
|
193 Custom error codes: \n |
|
194 SFW_ERROR_UNABLE_TO_OPEN_FILE in case can not open the XML file \n |
|
195 SFW_ERROR_INVALID_XML_FILE in case service registry is not a valid XML file \n |
|
196 SFW_ERROR_NO_SERVICE in case XML file has no service tag\n |
|
197 @return true if the metadata was read properly, false if there is an error |
|
198 */ |
|
199 bool ServiceMetaData::extractMetadata() |
|
200 { |
|
201 XQSERVICE_DEBUG_PRINT("ServiceMetaData::extractMetadata"); |
|
202 latestError = 0; |
|
203 clearMetadata(); |
|
204 version = ServiceMetaDataResults::VERSION_2; // default |
|
205 |
|
206 QXmlStreamReader xmlReader; |
|
207 bool parseError = false; |
|
208 //Open xml file |
|
209 if (!xmlDevice->isOpen() && !xmlDevice->open(QIODevice::ReadOnly)) { |
|
210 XQSERVICE_DEBUG_PRINT("XML error:Couldn't open the file"); |
|
211 latestError = ServiceMetaData::SFW_ERROR_UNABLE_TO_OPEN_FILE; |
|
212 parseError = true; |
|
213 } else { |
|
214 //Load xml content |
|
215 xmlReader.setDevice(xmlDevice); |
|
216 // Read XML doc |
|
217 while (!xmlReader.atEnd() && !parseError) { |
|
218 xmlReader.readNext(); |
|
219 //Found a <service> node, read service related metadata |
|
220 if (xmlReader.isStartElement() && xmlReader.name() == SERVICE_TAG) { |
|
221 |
|
222 // Support for previous XML version. Check if service element has name attribute |
|
223 // If so, assume the old element |
|
224 if (getAttributeValue(xmlReader, NAME_TAG, serviceName)) { |
|
225 if (!processServiceElementPrevVersion(xmlReader)) { |
|
226 XQSERVICE_DEBUG_PRINT("XML error: Couldn't process service element"); |
|
227 parseError = true; |
|
228 } |
|
229 } |
|
230 else if (!processServiceElement(xmlReader)) { |
|
231 XQSERVICE_DEBUG_PRINT("XML error: Couldn't process service element"); |
|
232 parseError = true; |
|
233 } |
|
234 } |
|
235 else if (xmlReader.isStartElement() && xmlReader.name() != SERVICE_TAG) { |
|
236 XQSERVICE_DEBUG_PRINT("XML error: No service"); |
|
237 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; |
|
238 parseError = true; |
|
239 } |
|
240 else if (xmlReader.tokenType() == QXmlStreamReader::Invalid) { |
|
241 XQSERVICE_DEBUG_PRINT("XML error: Invalid XML"); |
|
242 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
243 parseError = true; |
|
244 } |
|
245 } |
|
246 if (ownsXmlDevice) |
|
247 xmlDevice->close(); |
|
248 } |
|
249 if (parseError) { |
|
250 { |
|
251 XQSERVICE_DEBUG_PRINT("XML Parse error, line=%d,column=%d", xmlReader.lineNumber(), xmlReader.columnNumber()); |
|
252 clearMetadata(); |
|
253 } |
|
254 } |
|
255 XQSERVICE_DEBUG_PRINT("XML parseError: %d", parseError); |
|
256 return !parseError; |
|
257 } |
|
258 |
|
259 /* |
|
260 Gets the latest parsing error \n |
|
261 @return parsing error(negative value) or 0 in case there is none |
|
262 */ |
|
263 int ServiceMetaData::getLatestError() const |
|
264 { |
|
265 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getLatestError"); |
|
266 XQSERVICE_DEBUG_PRINT("latestError: %d", latestError); |
|
267 return latestError; |
|
268 } |
|
269 |
|
270 /* |
|
271 Parses and extracts the service from the current xml <service> node |
|
272 using the new format (Version 2) \n |
|
273 Schema: |
|
274 <!ELEMENT service ( name, filepath, description?, interface+ ) > |
|
275 <!ELEMENT description ( #CDATA ) > |
|
276 <!ELEMENT filepath ( #PCDATA ) > |
|
277 <!ELEMENT interface ( name, version, description?, capabilities?, customproperty* ) > |
|
278 <!ELEMENT capabilities ( #PCDATA ) > |
|
279 <!ELEMENT name ( #PCDATA ) > |
|
280 <!ELEMENT version ( #PCDATA ) > |
|
281 <!ELEMENT customproperty ( #CDATA ) > |
|
282 <!ATTLIST customproperty key NMTOKEN #REQUIRED > |
|
283 */ |
|
284 bool ServiceMetaData::processServiceElement(QXmlStreamReader &aXMLReader) |
|
285 { |
|
286 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processServiceElement"); |
|
287 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_TAG); |
|
288 bool parseError = false; |
|
289 |
|
290 int dupSTags[3] = {0 //->tag name |
|
291 ,0 //-> service description |
|
292 ,0 //-> filepath |
|
293 }; |
|
294 while(!parseError && !aXMLReader.atEnd()) { |
|
295 aXMLReader.readNext(); |
|
296 if (aXMLReader.isStartElement() && aXMLReader.name() == DESCRIPTION_TAG) { |
|
297 //Found <description> tag |
|
298 serviceDescription = aXMLReader.readElementText(); |
|
299 dupSTags[1]++; |
|
300 } else if (aXMLReader.isStartElement() && aXMLReader.name() == NAME_TAG) { |
|
301 serviceName = aXMLReader.readElementText(); |
|
302 dupSTags[0]++; |
|
303 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
304 //Found a <interface> node, read module related metadata |
|
305 if (!processInterfaceElement(aXMLReader)) |
|
306 parseError = true; |
|
307 } else if (aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_FILEPATH ) { |
|
308 //Found <filepath> tag |
|
309 dupSTags[2]++; |
|
310 serviceLocation = aXMLReader.readElementText(); |
|
311 } else if (aXMLReader.isStartElement() && aXMLReader.name() == "version") { |
|
312 //FOUND <version> tag on service level. We ignore this for now |
|
313 aXMLReader.readElementText(); |
|
314 } else if (aXMLReader.isEndElement() && aXMLReader.name() == SERVICE_TAG) { |
|
315 //Found </service>, leave the loop |
|
316 break; |
|
317 } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { |
|
318 latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; |
|
319 parseError = true; |
|
320 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
321 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
322 parseError = true; |
|
323 } |
|
324 } |
|
325 if ( !parseError ) { |
|
326 if (serviceName.isEmpty()) { |
|
327 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; |
|
328 parseError = true; |
|
329 } else if (serviceLocation.isEmpty()) { |
|
330 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_FILEPATH; |
|
331 parseError = true; |
|
332 } |
|
333 } |
|
334 |
|
335 for(int i=0;!parseError && i<3;i++) { |
|
336 if (dupSTags[i] > 1) { |
|
337 parseError = true; |
|
338 latestError = SFW_ERROR_DUPLICATED_TAG; |
|
339 break; |
|
340 } |
|
341 } |
|
342 |
|
343 //update all interfaces with service data |
|
344 const int icount = serviceInterfaces.count(); |
|
345 if (icount == 0 && latestError == 0) { |
|
346 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; |
|
347 parseError = true; |
|
348 } |
|
349 for (int i = 0; i<icount; i++) { |
|
350 serviceInterfaces.at(i).d->serviceName = serviceName; |
|
351 serviceInterfaces.at(i).d->properties[XQAiwInterfaceDescriptor::Location] = serviceLocation; |
|
352 serviceInterfaces.at(i).d->properties[XQAiwInterfaceDescriptor::ServiceDescription] = serviceDescription; |
|
353 } |
|
354 |
|
355 if (parseError) { |
|
356 clearMetadata(); |
|
357 } |
|
358 XQSERVICE_DEBUG_PRINT("processServiceElement parseError: %d", parseError); |
|
359 return !parseError; |
|
360 } |
|
361 |
|
362 /*! |
|
363 Parses and extracts the service from the current xml <service> node |
|
364 using the new format (Version 1) \n |
|
365 |
|
366 <!ELEMENT service ( description?, interface+ ) > |
|
367 <!ATTLIST service name #CDATA #REQUIRED > |
|
368 <!ATTLIST service filepath #CDATA #REQUIRED > |
|
369 <!ELEMENT description ( #CDATA ) > |
|
370 <!ELEMENT interface ( description? ) > |
|
371 <!ATTLIST interface name #CDATA #REQUIRED > |
|
372 <!ATTLIST interface version #CDATA #REQUIRED > |
|
373 <!ATTLIST interface capabilities #CDATA > |
|
374 |
|
375 Custom error codes: \n |
|
376 SFW_ERROR_NO_SERVICE_NAME in case no service name in XML file \n |
|
377 SFW_ERROR_NO_INTERFACE_VERSION in case no interface version in XML file \n |
|
378 SFW_ERROR_PARSE_SERVICE in case can not parse service section in XML file \n |
|
379 SFW_ERROR_NO_SERVICE_FILEPATH in case no service file path in XML file \n |
|
380 SFW_ERROR_INVALID_XML_FILE in case XML file is not valid \n |
|
381 SFW_ERROR_NO_SERVICE_INTERFACE in case no interface defined for service in XML file \n |
|
382 @param aXMLReader xml stream reader |
|
383 @return true if the metadata was read properly, false if there is an error |
|
384 |
|
385 |
|
386 */ |
|
387 bool ServiceMetaData::processServiceElementPrevVersion(QXmlStreamReader &aXMLReader) |
|
388 { |
|
389 version = ServiceMetaDataResults::VERSION_1; // Previous version |
|
390 |
|
391 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processServiceElementPrevVersion"); |
|
392 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_TAG); |
|
393 bool parseError = false; |
|
394 |
|
395 QString tmp; |
|
396 if (!getAttributeValue(aXMLReader, NAME_TAG, tmp)) { |
|
397 XQSERVICE_DEBUG_PRINT("No service name"); |
|
398 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; |
|
399 parseError = true; |
|
400 } |
|
401 |
|
402 if (!parseError) { |
|
403 if (!getAttributeValue(aXMLReader, SERVICE_FILEPATH, serviceLocation)) { |
|
404 XQSERVICE_DEBUG_PRINT("No service filepath"); |
|
405 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_FILEPATH; |
|
406 parseError = true; |
|
407 } |
|
408 } |
|
409 |
|
410 while (!parseError && !aXMLReader.atEnd()) { |
|
411 aXMLReader.readNext(); |
|
412 if (aXMLReader.name() == DESCRIPTION_TAG) { |
|
413 serviceDescription = aXMLReader.readElementText(); |
|
414 XQSERVICE_DEBUG_PRINT("serviceDescription: %s", qPrintable(serviceDescription)); |
|
415 //Found a <interface> node, read module related metadata |
|
416 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
417 if (!processInterfaceElementPrevVersion(aXMLReader)){ |
|
418 XQSERVICE_DEBUG_PRINT("Couldn't process interface element"); |
|
419 parseError = true; |
|
420 } |
|
421 //Found </service>, leave the loop |
|
422 } else if (aXMLReader.isEndElement() && aXMLReader.name() == SERVICE_TAG) { |
|
423 XQSERVICE_DEBUG_PRINT("Service element handled"); |
|
424 break; |
|
425 } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { |
|
426 XQSERVICE_DEBUG_PRINT("Service parse error"); |
|
427 latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; |
|
428 parseError = true; |
|
429 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
430 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
431 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
432 parseError = true; |
|
433 } |
|
434 } |
|
435 |
|
436 if (serviceInterfaces.count() == 0 && latestError == 0) { |
|
437 XQSERVICE_DEBUG_PRINT("No service interface"); |
|
438 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; |
|
439 parseError = true; |
|
440 } |
|
441 if (parseError) { |
|
442 clearMetadata(); |
|
443 } |
|
444 XQSERVICE_DEBUG_PRINT("parseError: %d", parseError); |
|
445 return !parseError; |
|
446 } |
|
447 |
|
448 |
|
449 /* |
|
450 Parses and extracts the interface metadata from the current xml <interface> node \n |
|
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 if (getAttributeValue(aXMLReader, INTERFACE_CAPABILITY, tmp)) { |
|
627 XQSERVICE_DEBUG_PRINT("Interface capability value"); |
|
628 aInterface.d->properties[XQAiwInterfaceDescriptor::Capabilities] = tmp.split(",", QString::SkipEmptyParts); |
|
629 } |
|
630 } else { |
|
631 XQSERVICE_DEBUG_PRINT("Invalid interface version"); |
|
632 latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION; |
|
633 parseError = true; |
|
634 } |
|
635 } |
|
636 else{ |
|
637 XQSERVICE_DEBUG_PRINT("No interface version"); |
|
638 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; |
|
639 parseError = true; |
|
640 } |
|
641 } else { |
|
642 XQSERVICE_DEBUG_PRINT("No interface name"); |
|
643 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; |
|
644 parseError = true; |
|
645 } |
|
646 |
|
647 while (!parseError && !aXMLReader.atEnd()) { |
|
648 aXMLReader.readNext(); |
|
649 //Read interface description |
|
650 if (aXMLReader.isStartElement() && aXMLReader.name() == DESCRIPTION_TAG) { |
|
651 XQSERVICE_DEBUG_PRINT("Interface description"); |
|
652 aInterface.d->properties[XQAiwInterfaceDescriptor::InterfaceDescription] = aXMLReader.readElementText(); |
|
653 //Found </interface>, leave the loop |
|
654 } else if (aXMLReader.isEndElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
655 XQSERVICE_DEBUG_PRINT("Interface handled"); |
|
656 break; |
|
657 } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) { |
|
658 XQSERVICE_DEBUG_PRINT("Interface parse error"); |
|
659 latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE; |
|
660 parseError = true; |
|
661 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
662 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
663 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
664 parseError = true; |
|
665 } |
|
666 } |
|
667 |
|
668 // Consistency check |
|
669 if (!parseError) { |
|
670 if (dupITags[1] == 0) { //no version tag found |
|
671 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; |
|
672 parseError = true; |
|
673 } else if (aInterface.d->interfaceName.isEmpty()) { |
|
674 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; |
|
675 parseError = true; |
|
676 } |
|
677 } |
|
678 |
|
679 if (!parseError) { |
|
680 const QString ident = aInterface.d->interfaceName |
|
681 + QString::number(aInterface.majorVersion()) |
|
682 + "." |
|
683 + QString::number(aInterface.minorVersion()); |
|
684 XQSERVICE_DEBUG_PRINT("ident: %s", qPrintable(ident)); |
|
685 if (duplicates.contains(ident.toLower())) { |
|
686 XQSERVICE_DEBUG_PRINT("Duplicate interface"); |
|
687 latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE; |
|
688 parseError = true; |
|
689 } else { |
|
690 duplicates.insert(ident.toLower()); |
|
691 serviceInterfaces.append(aInterface); |
|
692 if (!m_latestIndex.contains(aInterface.d->interfaceName.toLower()) |
|
693 || lessThan(latestInterfaceVersion(aInterface.d->interfaceName), aInterface)) |
|
694 |
|
695 { |
|
696 m_latestIndex[aInterface.d->interfaceName.toLower()] = serviceInterfaces.count() - 1; |
|
697 } |
|
698 } |
|
699 } |
|
700 |
|
701 if (parseError) |
|
702 { |
|
703 // Delete garbage |
|
704 delete aInterface.d; |
|
705 aInterface.d = 0; |
|
706 } |
|
707 |
|
708 XQSERVICE_DEBUG_PRINT("processInterfaceElementPrevVersion parseError: %d", parseError); |
|
709 return !parseError; |
|
710 } |
|
711 |
|
712 |
|
713 /*! |
|
714 Gets the value of the attribute from the XML node \n |
|
715 @param aDomElement xml node |
|
716 @param aAttributeName attribute name |
|
717 @param aValue [out] attribute value |
|
718 @return true if the value was read, false otherwise |
|
719 */ |
|
720 bool ServiceMetaData::getAttributeValue(const QXmlStreamReader &aXMLReader, const QString &aAttributeName, QString &aValue) |
|
721 { |
|
722 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getAttributeValue"); |
|
723 XQSERVICE_DEBUG_PRINT("aAttributeName: %s", qPrintable(aAttributeName)); |
|
724 bool result = false; |
|
725 for (int i = 0; i < aXMLReader.attributes().count(); i++){ |
|
726 QXmlStreamAttribute att = aXMLReader.attributes()[i]; |
|
727 if (att.name() == aAttributeName) { |
|
728 if (att.value().isNull() || att.value().isEmpty()) { |
|
729 result = false; |
|
730 } else { |
|
731 result = true; |
|
732 aValue = att.value().toString(); |
|
733 XQSERVICE_DEBUG_PRINT("aValue: %s", qPrintable(aValue)); |
|
734 } |
|
735 } |
|
736 } |
|
737 // Capability attribute is allowed to be empty |
|
738 if (aAttributeName == INTERFACE_CAPABILITY) { |
|
739 result = true; |
|
740 } |
|
741 XQSERVICE_DEBUG_PRINT("result: %d", result); |
|
742 return result; |
|
743 } |
|
744 |
|
745 |
|
746 XQAiwInterfaceDescriptor ServiceMetaData::latestInterfaceVersion(const QString &interfaceName) |
|
747 { |
|
748 XQAiwInterfaceDescriptor ret; |
|
749 if (m_latestIndex.contains(interfaceName.toLower())) |
|
750 return serviceInterfaces[m_latestIndex[interfaceName.toLower()]]; |
|
751 else |
|
752 return ret; |
|
753 } |
|
754 |
|
755 QList<XQAiwInterfaceDescriptor> ServiceMetaData::latestInterfaces() const |
|
756 { |
|
757 XQSERVICE_DEBUG_PRINT("ServiceMetaData::latestInterfaces"); |
|
758 QList<XQAiwInterfaceDescriptor> interfaces; |
|
759 QHash<QString,int>::const_iterator i = m_latestIndex.constBegin(); |
|
760 while(i != m_latestIndex.constEnd()) |
|
761 { |
|
762 interfaces.append(serviceInterfaces[i.value()]); |
|
763 ++i; |
|
764 } |
|
765 return interfaces; |
|
766 } |
|
767 |
|
768 bool ServiceMetaData::lessThan(const XQAiwInterfaceDescriptor &d1, |
|
769 const XQAiwInterfaceDescriptor &d2) const |
|
770 { |
|
771 return (d1.majorVersion() < d2.majorVersion()) |
|
772 || ( d1.majorVersion() == d2.majorVersion() |
|
773 && d1.minorVersion() < d2.minorVersion()); |
|
774 |
|
775 } |
|
776 |
|
777 bool ServiceMetaData::checkVersion(const QString &version) const |
|
778 { |
|
779 XQSERVICE_DEBUG_PRINT("ServiceMetaData::checkVersion"); |
|
780 //match x.y as version format |
|
781 QRegExp rx("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"); |
|
782 int pos = rx.indexIn(version); |
|
783 QStringList list = rx.capturedTexts(); |
|
784 bool success = false; |
|
785 if (pos == 0 && list.count() == 3 |
|
786 && rx.matchedLength() == version.length() ) |
|
787 { |
|
788 list[1].toInt(&success); |
|
789 if ( success ) { |
|
790 list[2].toInt(&success); |
|
791 } |
|
792 } |
|
793 XQSERVICE_DEBUG_PRINT("success: %d", success); |
|
794 return success; |
|
795 } |
|
796 |
|
797 void ServiceMetaData::transformVersion(const QString &version, int *major, int *minor) const |
|
798 { |
|
799 Q_ASSERT(major != NULL); |
|
800 Q_ASSERT(minor != NULL); |
|
801 if(!checkVersion(version)) { |
|
802 *major = -1; |
|
803 *minor = -1; |
|
804 } else { |
|
805 QRegExp rx("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"); |
|
806 rx.indexIn(version); |
|
807 QStringList list = rx.capturedTexts(); |
|
808 Q_ASSERT(list.count() == 3); |
|
809 *major = list[1].toInt(); |
|
810 *minor = list[2].toInt(); |
|
811 } |
|
812 } |
|
813 |
|
814 /* |
|
815 * Clears the service metadata |
|
816 * |
|
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 |