From fb547364c73c8d36e5b6ed0d2c74ded1e3447513 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 15:36:34 +0200 Subject: [PATCH] [packagechooser] Complete parsing of AppData - Document all the static inline methods that do the work - Fill up a QVariantMap from , and

elements, and use that to initialize the PackageItem. --- src/modules/packagechooser/PackageModel.cpp | 152 +++++++++++++++++++- 1 file changed, 147 insertions(+), 5 deletions(-) diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index 9a572f036..29890ee9b 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -23,6 +23,7 @@ #ifdef HAVE_XML #include +#include #include #endif @@ -98,7 +99,12 @@ PackageItem::PackageItem::PackageItem( const QVariantMap& item_map ) } #ifdef HAVE_XML -QDomDocument +/** @brief try to load the given @p fileName XML document + * + * Returns a QDomDocument, which will be valid iff the file can + * be read and contains valid XML data. + */ +static inline QDomDocument loadAppData( const QString& fileName ) { QFile file( fileName ); @@ -116,12 +122,138 @@ loadAppData( const QString& fileName ) return doc; } -QString +/** @brief gets the text of child element @p tagName + */ +static inline QString getChildText( const QDomNode& n, const QString& tagName ) { QDomElement e = n.firstChildElement( tagName ); return e.isNull() ? QString() : e.text(); } + +/** @brief Gets a suitable screenshot path + * + * The element contains zero or more + * elements, which can have a *type* associated with them. + * Scan the screenshot elements, return the path + * for the one labeled with type=default or, if there is no + * default, the first element. + */ +static inline QString +getScreenshotPath( const QDomNode& n ) +{ + QDomElement shotsNode = n.firstChildElement( "screenshots" ); + if ( shotsNode.isNull() ) + { + return QString(); + } + + const QDomNodeList shotList = shotsNode.childNodes(); + int firstScreenshot = -1; // Use which screenshot node? + for ( int i = 0; i < shotList.count(); ++i ) + { + if ( !shotList.at( i ).isElement() ) + { + continue; + } + QDomElement e = shotList.at( i ).toElement(); + if ( e.tagName() != "screenshot" ) + { + continue; + } + // If none has the "type=default" attribute, use the first one + if ( firstScreenshot < 0 ) + { + firstScreenshot = i; + } + // But type=default takes precedence. + if ( e.hasAttribute( "type" ) && e.attribute( "type" ) == "default" ) + { + firstScreenshot = i; + break; + } + } + + if ( firstScreenshot >= 0 ) + { + return shotList.at( firstScreenshot ).firstChildElement( "image" ).text(); + } + + return QString(); +} + +/** @brief Returns language of the given element @p e + * + * Transforms the attribute value for xml:lang to something + * suitable for TranslatedString (e.g. [lang]). + */ +static inline QString +getLanguage( const QDomElement& e ) +{ + QString language = e.attribute( "xml:lang" ); + if ( !language.isEmpty() ) + { + language.replace( '-', '_' ); + language.prepend( '[' ); + language.append( ']' ); + } + return language; +} + +/** @brief Scan the list of @p children for @p tagname elements and add them to the map + * + * Uses @p mapname instead of @p tagname for the entries in map @p m + * to allow renaming from XML to map keys (in particular for + * TranslatedString). Also transforms xml:lang attributes to suitable + * key-decorations on @p mapname. + */ +static inline void +fillMap( QVariantMap& m, const QDomNodeList& children, const QString& tagname, const QString& mapname ) +{ + for ( int i = 0; i < children.count(); ++i ) + { + if ( !children.at( i ).isElement() ) + { + continue; + } + + QDomElement e = children.at( i ).toElement(); + if ( e.tagName() != tagname ) + { + continue; + } + + m[ mapname + getLanguage( e ) ] = e.text(); + } +} + +/** @brief gets the and elements +* +* Builds up a map of the elements (which may have a *lang* +* attribute to indicate translations and paragraphs of the +* element (also with lang). Uses the

+* elements to supplement the description if no description +* is available for a given language. +* +* Returns a map with keys suitable for use by TranslatedString. +*/ +static inline QVariantMap +getNameAndSummary( const QDomNode& n ) +{ + QVariantMap m; + + const QDomNodeList children = n.childNodes(); + fillMap( m, children, "name", "name" ); + fillMap( m, children, "summary", "description" ); + + const QDomElement description = n.firstChildElement( "description" ); + if ( !description.isNull() ) + { + fillMap( m, description.childNodes(), "p", "description" ); + } + + return m; +} #endif PackageItem @@ -129,7 +261,7 @@ PackageItem::fromAppData( const QString& fileName ) { #ifdef HAVE_XML cDebug() << "Loading AppData XML from" << fileName; - + QDomDocument doc = loadAppData( fileName ); if ( doc.isNull() ) { @@ -140,13 +272,23 @@ PackageItem::fromAppData( const QString& fileName ) if ( !componentNode.isNull() && componentNode.tagName() == "component" ) { QString id = getChildText( componentNode, "id" ); - cDebug() << "Got AppData id" << id; + if ( id.isEmpty() ) + { + return PackageItem(); + } + + QString screenshotPath = getScreenshotPath( componentNode ); + + QVariantMap map = getNameAndSummary( componentNode ); + map.insert( "id", id ); + map.insert( "screenshot", screenshotPath ); + return PackageItem( map ); } return PackageItem(); #else cWarning() << "Loading AppData XML is not supported."; - + return PackageItem(); #endif }