qthighway/xqserviceutil/src/xqservicemetadata/xqservicemetadata.cpp
branchRCL_3
changeset 10 cd2778e5acfe
parent 9 5d007b20cfd0
child 11 19a54be74e5e
equal deleted inserted replaced
9:5d007b20cfd0 10:cd2778e5acfe
     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