|
1 /* |
|
2 * Copyright (c) 2008 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: Service metadata parser class implementation |
|
19 * |
|
20 */ |
|
21 |
|
22 #include "xqservicelog.h" |
|
23 |
|
24 #include <QFile> |
|
25 #include "xqservicemetadata_p.h" |
|
26 #include "xqsfwinterface_p.h" |
|
27 |
|
28 |
|
29 //XML tags and attributes |
|
30 //General |
|
31 #define NAME_TAG "name" |
|
32 #define DESCRIPTION_TAG "description" |
|
33 |
|
34 //Service related |
|
35 #define SERVICE_TAG "service" |
|
36 #define SERVICE_FILEPATH "filepath" |
|
37 |
|
38 //Interface related |
|
39 #define INTERFACE_TAG "interface" |
|
40 #define INTERFACE_VERSION "version" |
|
41 #define INTERFACE_CAPABILITY "capabilities" |
|
42 |
|
43 |
|
44 QT_BEGIN_NAMESPACE |
|
45 |
|
46 static const char PATH_SEPARATOR[] = "\\"; |
|
47 |
|
48 /*! |
|
49 \class ServiceMetaData |
|
50 |
|
51 Utility class (used by service database) that offers support for |
|
52 parsing metadata service xml registry file during service registration. \n |
|
53 |
|
54 It uses QXMLStreamReader class for parsing. Supproted Operations are: |
|
55 - Parse the service and interfaces defined in XML file |
|
56 - name, version, capabilitiesList, description and filePath of service can be retrieved |
|
57 - each interface can be retrieved |
|
58 */ |
|
59 |
|
60 /*! |
|
61 * Class constructor |
|
62 * |
|
63 * @param aXmlFilePath path to the xml file that describes the service. |
|
64 */ |
|
65 ServiceMetaData::ServiceMetaData(const QString &aXmlFilePath) |
|
66 { |
|
67 XQSERVICE_DEBUG_PRINT("ServiceMetaData::ServiceMetaData(1)"); |
|
68 XQSERVICE_DEBUG_PRINT("aXmlFilePath: %s", qPrintable(aXmlFilePath)); |
|
69 xmlDevice = new QFile(aXmlFilePath); |
|
70 ownsXmlDevice = true; |
|
71 latestError = 0; |
|
72 } |
|
73 |
|
74 /*! |
|
75 * Class constructor |
|
76 * |
|
77 * @param device QIODevice that contains the XML data that describes the service. |
|
78 */ |
|
79 ServiceMetaData::ServiceMetaData(QIODevice *device) |
|
80 { |
|
81 XQSERVICE_DEBUG_PRINT("ServiceMetaData::ServiceMetaData(2)"); |
|
82 xmlDevice = device; |
|
83 ownsXmlDevice = false; |
|
84 latestError = 0; |
|
85 } |
|
86 |
|
87 /*! |
|
88 * Class destructor |
|
89 * |
|
90 */ |
|
91 ServiceMetaData::~ServiceMetaData() |
|
92 { |
|
93 XQSERVICE_DEBUG_PRINT("ServiceMetaData::~ServiceMetaData"); |
|
94 if (ownsXmlDevice) |
|
95 delete xmlDevice; |
|
96 } |
|
97 |
|
98 /*! |
|
99 Sets the device containing the XML data that describes the service to \a device. |
|
100 */ |
|
101 void ServiceMetaData::setDevice(QIODevice *device) |
|
102 { |
|
103 XQSERVICE_DEBUG_PRINT("ServiceMetaData::setDevice"); |
|
104 clearMetadata(); |
|
105 xmlDevice = device; |
|
106 ownsXmlDevice = false; |
|
107 } |
|
108 |
|
109 /*! |
|
110 Returns the device containing the XML data that describes the service. |
|
111 */ |
|
112 QIODevice *ServiceMetaData::device() const |
|
113 { |
|
114 XQSERVICE_DEBUG_PRINT("ServiceMetaData::device"); |
|
115 return xmlDevice; |
|
116 } |
|
117 |
|
118 /*! |
|
119 * Gets the service name |
|
120 * |
|
121 * @return service name or default value (empty string) if it is not available |
|
122 */ |
|
123 QString ServiceMetaData::name() |
|
124 { |
|
125 XQSERVICE_DEBUG_PRINT("ServiceMetaData::name"); |
|
126 XQSERVICE_DEBUG_PRINT("serviceName: %s", qPrintable(serviceName)); |
|
127 return serviceName; |
|
128 } |
|
129 |
|
130 /*! |
|
131 * Sets the path of service implementation file |
|
132 * |
|
133 * @param aFilePath path of service implementation file |
|
134 */ |
|
135 void ServiceMetaData::setServiceFilePath(const QString &aFilePath) |
|
136 { |
|
137 XQSERVICE_DEBUG_PRINT("ServiceMetaData::setServiceFilePath"); |
|
138 XQSERVICE_DEBUG_PRINT("aFilePath: %s", qPrintable(aFilePath)); |
|
139 serviceFilePath = aFilePath; |
|
140 } |
|
141 |
|
142 /*! |
|
143 * Gets the path of the service implementation file |
|
144 * |
|
145 * @return service implementation filepath |
|
146 */ |
|
147 QString ServiceMetaData::filePath() |
|
148 { |
|
149 XQSERVICE_DEBUG_PRINT("ServiceMetaData::filePath"); |
|
150 XQSERVICE_DEBUG_PRINT("serviceFilePath: %s", qPrintable(serviceFilePath)); |
|
151 return serviceFilePath; |
|
152 } |
|
153 |
|
154 /*! |
|
155 * Gets the service description |
|
156 * |
|
157 * @return service description or default value (empty string) if it is not available |
|
158 */ |
|
159 QString ServiceMetaData::description() |
|
160 { |
|
161 XQSERVICE_DEBUG_PRINT("ServiceMetaData::description"); |
|
162 XQSERVICE_DEBUG_PRINT("serviceDescription: %s", qPrintable(serviceDescription)); |
|
163 return serviceDescription; |
|
164 } |
|
165 |
|
166 /*! |
|
167 Returns the number of interfaces provided by the service description |
|
168 */ |
|
169 int ServiceMetaData::interfaceCount() |
|
170 { |
|
171 XQSERVICE_DEBUG_PRINT("ServiceMetaData::interfaceCount"); |
|
172 XQSERVICE_DEBUG_PRINT("serviceInterfaces.count(): %d", serviceInterfaces.count()); |
|
173 return serviceInterfaces.count(); |
|
174 } |
|
175 |
|
176 /*! |
|
177 Returns the metadata of the interace at \a index; otherwise |
|
178 returns 0. |
|
179 */ |
|
180 QList<SFWInterface> ServiceMetaData::getInterfaces() |
|
181 { |
|
182 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getInterfaces"); |
|
183 return serviceInterfaces; |
|
184 } |
|
185 |
|
186 /*! |
|
187 Parses the file and extracts the service metadata \n |
|
188 Custom error codes: \n |
|
189 SFW_ERROR_UNABLE_TO_OPEN_FILE in case can not open the XML file \n |
|
190 SFW_ERROR_INVALID_XML_FILE in case service registry is not a valid XML file \n |
|
191 SFW_ERROR_NO_SERVICE in case XML file has no service tag\n |
|
192 @return true if the metadata was read properly, false if there is an error |
|
193 */ |
|
194 bool ServiceMetaData::extractMetadata() |
|
195 { |
|
196 XQSERVICE_DEBUG_PRINT("ServiceMetaData::extractMetadata"); |
|
197 latestError = 0; |
|
198 clearMetadata(); |
|
199 QXmlStreamReader xmlReader; |
|
200 bool parseError = false; |
|
201 //Open xml file |
|
202 if (!xmlDevice->isOpen() && !xmlDevice->open(QIODevice::ReadOnly)) { |
|
203 XQSERVICE_DEBUG_PRINT("Couldn't open the file"); |
|
204 latestError = ServiceMetaData::SFW_ERROR_UNABLE_TO_OPEN_FILE; |
|
205 parseError = true; |
|
206 } else { |
|
207 //Load xml content |
|
208 xmlReader.setDevice(xmlDevice); |
|
209 // Read XML doc |
|
210 while (!xmlReader.atEnd() && !parseError) { |
|
211 xmlReader.readNext(); |
|
212 //Found a <service> node, read service related metadata |
|
213 if (xmlReader.isStartElement() && xmlReader.name() == SERVICE_TAG) { |
|
214 if (!processServiceElement(xmlReader)) { |
|
215 XQSERVICE_DEBUG_PRINT("Couldn't process service element"); |
|
216 parseError = true; |
|
217 } |
|
218 } |
|
219 else if (xmlReader.isStartElement() && xmlReader.name() != SERVICE_TAG) { |
|
220 XQSERVICE_DEBUG_PRINT("No service"); |
|
221 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; |
|
222 parseError = true; |
|
223 } |
|
224 else if (xmlReader.tokenType() == QXmlStreamReader::Invalid) { |
|
225 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
226 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
227 parseError = true; |
|
228 } |
|
229 } |
|
230 if (ownsXmlDevice) |
|
231 xmlDevice->close(); |
|
232 } |
|
233 if (parseError) { |
|
234 clearMetadata(); |
|
235 } |
|
236 XQSERVICE_DEBUG_PRINT("parseError: %d", parseError); |
|
237 return !parseError; |
|
238 } |
|
239 |
|
240 /*! |
|
241 Gets the latest parsing error \n |
|
242 @return parsing error(negative value) or 0 in case there is none |
|
243 */ |
|
244 int ServiceMetaData::getLatestError() |
|
245 { |
|
246 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getLatestError"); |
|
247 XQSERVICE_DEBUG_PRINT("latestError: %d", latestError); |
|
248 return latestError; |
|
249 } |
|
250 |
|
251 /*! |
|
252 Gets the value of the attribute from the XML node \n |
|
253 @param aDomElement xml node |
|
254 @param aAttributeName attribute name |
|
255 @param aValue [out] attribute value |
|
256 @return true if the value was read, false otherwise |
|
257 */ |
|
258 bool ServiceMetaData::getAttributeValue(const QXmlStreamReader &aXMLReader, const QString &aAttributeName, QString &aValue) |
|
259 { |
|
260 XQSERVICE_DEBUG_PRINT("ServiceMetaData::getAttributeValue"); |
|
261 XQSERVICE_DEBUG_PRINT("aAttributeName: %s", qPrintable(aAttributeName)); |
|
262 bool result = false; |
|
263 for (int i = 0; i < aXMLReader.attributes().count(); i++){ |
|
264 QXmlStreamAttribute att = aXMLReader.attributes()[i]; |
|
265 if (att.name() == aAttributeName) { |
|
266 if (att.value().isNull() || att.value().isEmpty()) { |
|
267 result = false; |
|
268 } else { |
|
269 result = true; |
|
270 aValue = att.value().toString(); |
|
271 XQSERVICE_DEBUG_PRINT("aValue: %s", qPrintable(aValue)); |
|
272 } |
|
273 } |
|
274 } |
|
275 // Capability attribute is allowed to be empty |
|
276 if (aAttributeName == INTERFACE_CAPABILITY) { |
|
277 result = true; |
|
278 } |
|
279 XQSERVICE_DEBUG_PRINT("result: %d", result); |
|
280 return result; |
|
281 } |
|
282 |
|
283 /*! |
|
284 Parses and extracts the service metadata from the current xml <service> node \n |
|
285 Custom error codes: \n |
|
286 SFW_ERROR_NO_SERVICE_NAME in case no service name in XML file \n |
|
287 SFW_ERROR_NO_INTERFACE_VERSION in case no interface version in XML file \n |
|
288 SFW_ERROR_PARSE_SERVICE in case can not parse service section in XML file \n |
|
289 SFW_ERROR_NO_SERVICE_FILEPATH in case no service file path in XML file \n |
|
290 SFW_ERROR_INVALID_XML_FILE in case XML file is not valid \n |
|
291 SFW_ERROR_NO_SERVICE_INTERFACE in case no interface defined for service in XML file \n |
|
292 @param aXMLReader xml stream reader |
|
293 @return true if the metadata was read properly, false if there is an error |
|
294 */ |
|
295 bool ServiceMetaData::processServiceElement(QXmlStreamReader &aXMLReader) |
|
296 { |
|
297 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processServiceElement"); |
|
298 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == SERVICE_TAG); |
|
299 bool parseError = false; |
|
300 |
|
301 if (!getAttributeValue(aXMLReader, NAME_TAG, serviceName)) { |
|
302 XQSERVICE_DEBUG_PRINT("No service name"); |
|
303 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; |
|
304 parseError = true; |
|
305 } |
|
306 |
|
307 if (!parseError) { |
|
308 if (!getAttributeValue(aXMLReader, SERVICE_FILEPATH, serviceFilePath)) { |
|
309 XQSERVICE_DEBUG_PRINT("No service filepath"); |
|
310 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_FILEPATH; |
|
311 parseError = true; |
|
312 } |
|
313 } |
|
314 |
|
315 while (!parseError && !aXMLReader.atEnd()) { |
|
316 aXMLReader.readNext(); |
|
317 if (aXMLReader.name() == DESCRIPTION_TAG) { |
|
318 serviceDescription = aXMLReader.readElementText(); |
|
319 XQSERVICE_DEBUG_PRINT("serviceDescription: %s", qPrintable(serviceDescription)); |
|
320 //Found a <interface> node, read module related metadata |
|
321 } else if (aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
322 if (!processInterfaceElement(aXMLReader)){ |
|
323 XQSERVICE_DEBUG_PRINT("Couldn't process interface element"); |
|
324 parseError = true; |
|
325 } |
|
326 //Found </service>, leave the loop |
|
327 } else if (aXMLReader.isEndElement() && aXMLReader.name() == SERVICE_TAG) { |
|
328 XQSERVICE_DEBUG_PRINT("Service element handled"); |
|
329 break; |
|
330 } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { |
|
331 XQSERVICE_DEBUG_PRINT("Service parse error"); |
|
332 latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; |
|
333 parseError = true; |
|
334 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
335 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
336 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
337 parseError = true; |
|
338 } |
|
339 } |
|
340 |
|
341 if (serviceInterfaces.count() == 0 && latestError == 0) { |
|
342 XQSERVICE_DEBUG_PRINT("No service interface"); |
|
343 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; |
|
344 parseError = true; |
|
345 } |
|
346 if (parseError) { |
|
347 clearMetadata(); |
|
348 } |
|
349 XQSERVICE_DEBUG_PRINT("parseError: %d", parseError); |
|
350 return !parseError; |
|
351 } |
|
352 |
|
353 /*! |
|
354 Parses and extracts the interface metadata from the current xml <interface> node \n |
|
355 Custome error codes: \n |
|
356 SFW_ERROR_NO_INTERFACE_NAME in case no interface name in XML file \n |
|
357 SFW_ERROR_PARSE_INTERFACE in case error parsing interface section \n |
|
358 SFW_ERROR_INVALID_XML_FILE in case XML file is not valid \n |
|
359 @param aXMLReader xml stream reader |
|
360 @return true if the metadata was read properly, false if there is an error |
|
361 */ |
|
362 bool ServiceMetaData::processInterfaceElement(QXmlStreamReader &aXMLReader) |
|
363 { |
|
364 XQSERVICE_DEBUG_PRINT("ServiceMetaData::processInterfaceElement"); |
|
365 Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == INTERFACE_TAG); |
|
366 bool parseError = false; |
|
367 |
|
368 //Read interface parameter |
|
369 QString tmp; |
|
370 SFWInterface aInterface(""); |
|
371 if (getAttributeValue(aXMLReader, NAME_TAG, tmp)) { |
|
372 XQSERVICE_DEBUG_PRINT("Name attribute value"); |
|
373 aInterface = SFWInterface(tmp); |
|
374 tmp.clear(); |
|
375 if (getAttributeValue(aXMLReader, INTERFACE_VERSION, tmp)) { |
|
376 XQSERVICE_DEBUG_PRINT("Interface version value"); |
|
377 bool success = checkVersion(tmp); |
|
378 if ( success ) { |
|
379 aInterface.setVersion(tmp); |
|
380 tmp.clear(); |
|
381 if (getAttributeValue(aXMLReader, INTERFACE_CAPABILITY, tmp)) { |
|
382 XQSERVICE_DEBUG_PRINT("Interface capability value"); |
|
383 aInterface.setCapabilities(tmp.split(",", QString::SkipEmptyParts)); |
|
384 } |
|
385 } else { |
|
386 XQSERVICE_DEBUG_PRINT("Invalid interface version"); |
|
387 latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION; |
|
388 parseError = true; |
|
389 } |
|
390 } |
|
391 else{ |
|
392 XQSERVICE_DEBUG_PRINT("No interface version"); |
|
393 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; |
|
394 parseError = true; |
|
395 } |
|
396 } else { |
|
397 XQSERVICE_DEBUG_PRINT("No interface name"); |
|
398 latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; |
|
399 parseError = true; |
|
400 } |
|
401 |
|
402 while (!parseError && !aXMLReader.atEnd()) { |
|
403 aXMLReader.readNext(); |
|
404 //Read interface description |
|
405 if (aXMLReader.isStartElement() && aXMLReader.name() == DESCRIPTION_TAG) { |
|
406 XQSERVICE_DEBUG_PRINT("Interface description"); |
|
407 aInterface.setDescription(aXMLReader.readElementText()); |
|
408 //Found </interface>, leave the loop |
|
409 } else if (aXMLReader.isEndElement() && aXMLReader.name() == INTERFACE_TAG) { |
|
410 XQSERVICE_DEBUG_PRINT("Interface handled"); |
|
411 break; |
|
412 } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) { |
|
413 XQSERVICE_DEBUG_PRINT("Interface parse error"); |
|
414 latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE; |
|
415 parseError = true; |
|
416 } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { |
|
417 XQSERVICE_DEBUG_PRINT("Invalid XML"); |
|
418 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; |
|
419 parseError = true; |
|
420 } |
|
421 } |
|
422 |
|
423 if (!parseError) { |
|
424 const QString ident = aInterface.name()+aInterface.version(); |
|
425 XQSERVICE_DEBUG_PRINT("ident: %s", qPrintable(ident)); |
|
426 if (duplicates.contains(ident.toLower())) { |
|
427 XQSERVICE_DEBUG_PRINT("Duplicate interface"); |
|
428 latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE; |
|
429 parseError = true; |
|
430 } else { |
|
431 duplicates.insert(ident.toLower()); |
|
432 serviceInterfaces.append(aInterface); |
|
433 } |
|
434 } |
|
435 XQSERVICE_DEBUG_PRINT("parseError: %d", parseError); |
|
436 return !parseError; |
|
437 } |
|
438 |
|
439 bool ServiceMetaData::checkVersion(const QString &version) |
|
440 { |
|
441 XQSERVICE_DEBUG_PRINT("ServiceMetaData::checkVersion"); |
|
442 //match x.y as version format |
|
443 QRegExp rx("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$"); |
|
444 int pos = rx.indexIn(version); |
|
445 QStringList list = rx.capturedTexts(); |
|
446 bool success = false; |
|
447 if (pos == 0 && list.count() == 3 |
|
448 && rx.matchedLength() == version.length() ) |
|
449 { |
|
450 list[1].toInt(&success); |
|
451 if ( success ) { |
|
452 list[2].toInt(&success); |
|
453 } |
|
454 } |
|
455 XQSERVICE_DEBUG_PRINT("success: %d", success); |
|
456 return success; |
|
457 } |
|
458 |
|
459 /*! |
|
460 * Clears the service metadata |
|
461 * |
|
462 */ |
|
463 void ServiceMetaData::clearMetadata() |
|
464 { |
|
465 XQSERVICE_DEBUG_PRINT("ServiceMetaData::clearMetadata"); |
|
466 serviceName.clear(); |
|
467 serviceFilePath.clear(); |
|
468 serviceDescription.clear(); |
|
469 serviceInterfaces.clear(); |
|
470 duplicates.clear(); |
|
471 } |
|
472 |
|
473 QT_END_NAMESPACE |