diff options
Diffstat (limited to 'installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp')
-rw-r--r-- | installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp new file mode 100644 index 000000000..c6ce195ea --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp @@ -0,0 +1,566 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterpackagesinfo.h" +#include "kdupdaterapplication.h" + +#include <QFileInfo> +#include <QDomDocument> +#include <QDomElement> +#include <QVector> + +using namespace KDUpdater; + +/*! + \ingroup kdupdater + \class KDUpdater::PackagesInfo kdupdaterpackagesinfo.h KDUpdaterPackagesInfo + \brief Provides access to information about packages installed on the application side. + + This class parses the XML package file specified via the setFileName() method and + provides access to the the information defined within the package file through an + easy to use API. You can: + \li get application name via the \ref applicationName() method + \li get application version via the \ref applicationVersion() method + \li get information about the number of packages installed and their meta-data via the + \ref packageInfoCount() and \ref packageInfo() methods. + + Instances of this class cannot be created. Each instance of \ref KDUpdater::Application + has one instance of this class associated with it. You can fetch a pointer to an instance + of this class for an application via the \ref KDUpdater::Application::packagesInfo() + method. +*/ + +/*! \enum UpdatePackagesInfo::Error + * Error codes related to retrieving update sources + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::NoError + * No error occurred + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::NotYetReadError + * The package information was not parsed yet from the XML file + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::CouldNotReadPackageFileError + * the specified update source file could not be read (does not exist or not readable) + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::InvalidXmlError + * The source file contains invalid XML. + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::InvalidContentError + * The source file contains valid XML, but does not match the expected format for package descriptions + */ + +struct PackagesInfo::PackagesInfoData +{ + PackagesInfoData( PackagesInfo* qq ) : + q( qq ), + application(0), + error(PackagesInfo::NotYetReadError), + compatLevel(-1), + modified( false ) + {} + PackagesInfo* q; + Application* application; + QString errorMessage; + PackagesInfo::Error error; + QString fileName; + QString applicationName; + QString applicationVersion; + int compatLevel; + bool modified; + + QVector<PackageInfo> packageInfoList; + + void addPackageFrom(const QDomElement& packageE); + void setInvalidContentError( const QString& detail ); +}; + +void PackagesInfo::PackagesInfoData::setInvalidContentError(const QString& detail) +{ + error = PackagesInfo::InvalidContentError; + errorMessage = tr("%1 contains invalid content: %2").arg(fileName, detail); +} + +/*! + \internal +*/ +PackagesInfo::PackagesInfo(Application* application) + : QObject(application), + d( new PackagesInfoData( this ) ) +{ + d->application = application; +} + +/*! + \internal +*/ +PackagesInfo::~PackagesInfo() +{ + writeToDisk(); + delete d; +} + +/*! + Returns a pointer to the application, whose package information this class provides + access to. +*/ +Application* PackagesInfo::application() const +{ + return d->application; +} + +/*! + Returns true if the PackagesInfo are valid else false is returned in which case + the \a errorString() method can be used to receive a describing error message. +*/ +bool PackagesInfo::isValid() const +{ + return d->error == NoError; +} + +/*! + Returns a human-readable error message. +*/ +QString PackagesInfo::errorString() const +{ + return d->errorMessage; +} + +PackagesInfo::Error PackagesInfo::error() const +{ + return d->error; +} + +/*! + Sets the complete file name of the Packages.xml file. The function also issues a call to + \ref refresh() to reload package information from the XML file. + + \sa KDUpdater::Application::setPackagesXMLFileName() +*/ +void PackagesInfo::setFileName(const QString& fileName) +{ + if( d->fileName == fileName ) + return; + + d->fileName = fileName; + refresh(); +} + +/*! + Returns the name of the Packages.xml file that this class referred to. +*/ +QString PackagesInfo::fileName() const +{ + return d->fileName; +} + +/*! + Sets the application name. By default this is the name specified in + the ApplicationName XML element of the Packages.xml file. +*/ +void PackagesInfo::setApplicationName(const QString& name) +{ + d->applicationName = name; + d->modified = true; +} + +/*! + Returns the application name. +*/ +QString PackagesInfo::applicationName() const +{ + return d->applicationName; +} + +/*! + Sets the application version. By default this is the version specified + in the ApplicationVersion XML element of Packages.xml. +*/ +void PackagesInfo::setApplicationVersion(const QString& version) +{ + d->applicationVersion = version; + d->modified = true; +} + +/*! + Returns the application version. +*/ +QString PackagesInfo::applicationVersion() const +{ + return d->applicationVersion; +} + +/*! + Returns the number of \ref KDUpdater::PackageInfo objects contained in this class. +*/ +int PackagesInfo::packageInfoCount() const +{ + return d->packageInfoList.count(); +} + +/*! + Returns the package info structure (\ref KDUpdater::PackageInfo) at index. If index is + out of range then an empty package info structure is returned. +*/ +PackageInfo PackagesInfo::packageInfo(int index) const +{ + if( index < 0 || index >= d->packageInfoList.count() ) + return PackageInfo(); + + return d->packageInfoList[index]; +} + +/*! + Returns the compat level of the application. +*/ +int PackagesInfo::compatLevel() const +{ + return d->compatLevel; +} + +/*! + This function returns the index of the package whose name is \c pkgName. If no such + package was found, this function returns -1. +*/ +int PackagesInfo::findPackageInfo(const QString& pkgName) const +{ + for(int i=0; i<d->packageInfoList.count(); i++) + { + if( d->packageInfoList[i].name == pkgName ) + return i; + } + + return -1; +} + +/*! + Returns all package info structures. +*/ +QVector<PackageInfo> PackagesInfo::packageInfos() const +{ + return d->packageInfoList; +} + +/*! + This function re-reads the Packages.xml file and updates itself. Changes to \ref applicationName() + and \ref applicationVersion() are lost after this function returns. The function emits a reset() + signal after completion. +*/ +void PackagesInfo::refresh() +{ + // First clear internal variables + d->applicationName.clear(); + d->applicationVersion.clear(); + d->packageInfoList.clear(); + d->modified = false; + + QFile file( d->fileName ); + + // if the file does not exist then we just skip the reading + if( !file.exists() ) + { + d->error = NoError; + d->errorMessage.clear(); + emit reset(); + return; + } + + // Open Packages.xml + if( !file.open(QFile::ReadOnly) ) + { + d->error = CouldNotReadPackageFileError; + d->errorMessage = tr("Could not read \"%1\"").arg(d->fileName); + emit reset(); + return; + } + + // Parse the XML document + QDomDocument doc; + QString parseErrorMessage; + int parseErrorLine; + int parseErrorColumn; + if( !doc.setContent( &file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn ) ) + { + d->error = InvalidXmlError; + d->errorMessage = tr("Parse error in %1 at %2, %3: %4") + .arg(d->fileName, + QString::number(parseErrorLine), + QString::number(parseErrorColumn), + parseErrorMessage); + emit reset(); + return; + } + file.close(); + + // Now populate information from the XML file. + QDomElement rootE = doc.documentElement(); + if( rootE.tagName() != QLatin1String( "Packages" ) ) + { + d->setInvalidContentError(tr("root element %1 unexpected, should be \"Packages\"").arg(rootE.tagName())); + emit reset(); + return; + } + + QDomNodeList childNodes = rootE.childNodes(); + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.item(i); + QDomElement childNodeE = childNode.toElement(); + if( childNodeE.isNull() ) + continue; + + if( childNodeE.tagName() == QLatin1String( "ApplicationName" ) ) + d->applicationName = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "ApplicationVersion" ) ) + d->applicationVersion = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Package" ) ) + d->addPackageFrom( childNodeE ); + else if( childNodeE.tagName() == QLatin1String( "CompatLevel" ) ) + d->compatLevel = childNodeE.text().toInt(); + } + + d->error = NoError; + d->errorMessage.clear(); + emit reset(); +} + +/*! + Sets the application compat level. +*/ +void PackagesInfo::setCompatLevel(int level) +{ + d->compatLevel = level; + d->modified = true; +} + +/*! + Marks the package with \a name as installed in \a version. + */ +bool PackagesInfo::installPackage( const QString& name, const QString& version, const QString& title, const QString& description + , const QStringList& dependencies, bool forcedInstallation, bool virtualComp, quint64 uncompressedSize ) +{ + if( findPackageInfo( name ) != -1 ) + return updatePackage( name, version, QDate::currentDate() ); + + PackageInfo info; + info.name = name; + info.version = version; + info.installDate = QDate::currentDate(); + info.title = title; + info.description = description; + info.dependencies = dependencies; + info.forcedInstallation = forcedInstallation; + info.virtualComp = virtualComp; + info.uncompressedSize = uncompressedSize; + d->packageInfoList.push_back( info ); + d->modified = true; + return true; +} + +/*! + Update the package. +*/ +bool PackagesInfo::updatePackage(const QString &name, + const QString &version, + const QDate &date) +{ + int index = findPackageInfo(name); + + if (index==-1) return false; + + d->packageInfoList[index].version = version; + d->packageInfoList[index].lastUpdateDate = date; + d->modified = true; + return true; +} + +/*! + Remove the package with \a name. + */ +bool PackagesInfo::removePackage( const QString& name ) +{ + const int index = findPackageInfo( name ); + if( index == -1 ) + return false; + + d->packageInfoList.remove( index ); + d->modified = true; + return true; +} + +static void addTextChildHelper(QDomNode *node, + const QString &tag, + const QString &text) +{ + QDomElement domElement = node->ownerDocument().createElement(tag); + QDomText domText = node->ownerDocument().createTextNode(text); + + domElement.appendChild(domText); + node->appendChild(domElement); +} + +void PackagesInfo::writeToDisk() +{ + if( d->modified && ( !d->packageInfoList.isEmpty() || QFile::exists( d->fileName ) ) ) + { + QDomDocument doc; + QDomElement root = doc.createElement(QLatin1String( "Packages") ) ; + doc.appendChild(root); + + addTextChildHelper(&root, QLatin1String( "ApplicationName" ), d->applicationName); + addTextChildHelper(&root, QLatin1String( "ApplicationVersion" ), d->applicationVersion); + if (d->compatLevel!=-1) { + addTextChildHelper(&root, QLatin1String( "CompatLevel" ), QString::number(d->compatLevel)); + } + + Q_FOREACH (const PackageInfo &info, d->packageInfoList) { + QDomElement package = doc.createElement( QLatin1String( "Package" ) ); + + addTextChildHelper( &package, QLatin1String( "Name" ), info.name ); + addTextChildHelper( &package, QLatin1String( "Pixmap" ), info.pixmap ); + addTextChildHelper( &package, QLatin1String( "Title" ), info.title ); + addTextChildHelper( &package, QLatin1String( "Description" ), info.description ); + addTextChildHelper( &package, QLatin1String( "Version" ), info.version ); + addTextChildHelper( &package, QLatin1String( "LastUpdateDate" ), info.lastUpdateDate.toString( Qt::ISODate ) ); + addTextChildHelper( &package, QLatin1String( "InstallDate" ), info.installDate.toString( Qt::ISODate) ); + addTextChildHelper( &package, QLatin1String( "Size" ), QString::number( info.uncompressedSize ) ); + QString assembledDependencies = QLatin1String( "" ); + Q_FOREACH( const QString & val, info.dependencies ){ + assembledDependencies += val + QLatin1String( "," ); + } + if ( info.dependencies.count() > 0 ) + assembledDependencies.chop( 1 ); + addTextChildHelper( &package, QLatin1String( "Dependencies" ), assembledDependencies ); + if ( info.forcedInstallation ) + addTextChildHelper( &package, QLatin1String( "ForcedInstallation" ), QLatin1String( "true" ) ); + if ( info.virtualComp ) + addTextChildHelper( &package, QLatin1String( "Virtual" ), QLatin1String( "true" ) ); + + root.appendChild(package); + } + + // Open Packages.xml + QFile file( d->fileName ); + if( !file.open(QFile::WriteOnly) ) { + return; + } + + file.write(doc.toByteArray(4)); + file.close(); + d->modified = false; + } +} + +void PackagesInfo::PackagesInfoData::addPackageFrom(const QDomElement& packageE) +{ + if( packageE.isNull() ) + return; + + QDomNodeList childNodes = packageE.childNodes(); + if(childNodes.count() == 0) + return; + + PackageInfo info; + info.forcedInstallation = false; + info.virtualComp = false; + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.item(i); + QDomElement childNodeE = childNode.toElement(); + if( childNodeE.isNull() ) + continue; + + if( childNodeE.tagName() == QLatin1String( "Name" ) ) + info.name = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Pixmap" ) ) + info.pixmap = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Title" ) ) + info.title = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Description" ) ) + info.description = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Version" ) ) + info.version = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Virtual" ) ) + info.virtualComp = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false; + else if( childNodeE.tagName() == QLatin1String( "Size" ) ) + info.uncompressedSize = childNodeE.text().toULongLong(); + else if( childNodeE.tagName() == QLatin1String( "Dependencies" ) ) + info.dependencies = childNodeE.text().split( QLatin1String( "," ) ); + else if( childNodeE.tagName() == QLatin1String( "ForcedInstallation" ) ) + info.forcedInstallation = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false; + else if( childNodeE.tagName() == QLatin1String( "LastUpdateDate" ) ) + info.lastUpdateDate = QDate::fromString(childNodeE.text(), Qt::ISODate); + else if( childNodeE.tagName() == QLatin1String( "InstallDate" ) ) + info.installDate = QDate::fromString(childNodeE.text(), Qt::ISODate); + } + + this->packageInfoList.append( info ); +} + +/*! + \fn void KDUpdater::PackagesInfo::reset() + + This signal is emitted whenever the contents of this class is refreshed, usually from within + the \ref refresh() slot. +*/ + +/*! + \ingroup kdupdater + \struct KDUpdater::PackageInfo kdupdaterpackagesinfo.h KDUpdaterPackageInfo + \brief Describes a single installed package in the application. + + This structure contains information about a single installed package in the application. + The information contained in this structure corresponds to the information described + by the Package XML element in Packages.xml +*/ + +/*! + \var QString KDUpdater::PackageInfo::name +*/ + +/*! + \var QString KDUpdater::PackageInfo::pixmap +*/ + +/*! + \var QString KDUpdater::PackageInfo::title +*/ + +/*! + \var QString KDUpdater::PackageInfo::description +*/ + +/*! + \var QString KDUpdater::PackageInfo::version +*/ + +/*! + \var QDate KDUpdater::PackageInfo::lastUpdateDate +*/ + +/*! + \var QDate KDUpdater::PackageInfo::installDate +*/ |