Commit 033b7806 authored by Thomas Friedrichsmeier's avatar Thomas Friedrichsmeier
Browse files

Refactor version parsing

parent e22a4a2a
Pipeline #173202 passed with stage
in 5 minutes and 35 seconds
......@@ -29,6 +29,7 @@ SET(misc_STAT_SRCS
rkdialogbuttonbox.cpp
rkoutputdirectory.cpp
rkstyle.cpp
rkparsedversion.cpp
)
ADD_LIBRARY(misc STATIC ${misc_STAT_SRCS})
......
/*
rkparsedversion - This file is part of the RKWard project. Created: Sat May 07 2022
SPDX-FileCopyrightText: 2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "rkparsedversion.h"
#include "../debug.h"
RKParsedVersion::RKParsedVersion(const QString& version) {
quint32 ret = 0;
int pos = -1;
int opos = 0;
for (int i = 3; i >= 0; --i) {
while (true) {
++pos;
if (!(pos < version.size () && version[pos].isDigit ())) {
int val = version.midRef(opos, pos - opos).toInt();
if ((val < 0) || (val > 255) || (pos == opos)) {
RK_DEBUG (MISC, DL_ERROR, "Invalid version specification '%s'", qPrintable (version));
if (val > 255) val = 255;
else val = 0;
}
ret += val << (8 * i);
if ((pos < version.size ()) && (version[pos] == '.')) {
opos = pos + 1;
break;
}
opos = pos;
i = -1;
break;
}
}
}
if (opos <= (version.size() - 1)) {
version_suffix = version.mid(opos);
}
version_numeric = ret;
}
QString RKParsedVersion::toString() const {
QString ret;
for (int i = 3; i >= 0; --i) {
int ver_part = (version_numeric >> (i * 8)) & 0x000000FF;
ret.append(QString::number(ver_part));
if (i > 0) ret.append('.');
}
if (ret.endsWith(QLatin1String(".0"))) ret.chop(2); // HACK: Don't print more than three version parts, unless the fourth is non-zero
if (!version_suffix.isNull()) ret.append('.' + version_suffix);
return ret;
}
/*
rkparsedversion - This file is part of the RKWard project. Created: Sat May 07 2022
SPDX-FileCopyrightText: 2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef RKPARSEDVERSION_H
#define RKPARSEDVERSION_H
#include <QString>
/** Helper class to compare versions. A version number of "a.b.c.d.e-fghi" is split into up to four numeric portions (stored as four bytes in a single 32bit unsigned int).
Anything else (everything after the fourth dot, or after the first character that is neither dot, nor digit) is stored as a string, and compared lexically. */
class RKParsedVersion {
public:
RKParsedVersion(const QString& from_string);
RKParsedVersion() : version_numeric(0) {};
/** Create a null version that will always compare as higher than other (non-null) versions */
static RKParsedVersion maxVersion() {
RKParsedVersion ret;
ret.version_numeric = 0xFFFFFFFF;
return ret;
}
bool operator >(const RKParsedVersion &other) const {
return (version_numeric > other.version_numeric || (version_numeric == other.version_numeric && version_suffix > other.version_suffix));
}
bool operator <(const RKParsedVersion &other) const {
return (version_numeric < other.version_numeric || (version_numeric == other.version_numeric && version_suffix < other.version_suffix));
}
bool operator ==(const RKParsedVersion &other) const {
return ((version_numeric == other.version_numeric) && (version_suffix == other.version_suffix));
}
bool isNull() const { return (version_suffix.isNull() && (version_numeric == 0 || version_numeric == 0xFFFFFFFF)); }
QString toString() const;
private:
quint32 version_numeric;
QString version_suffix;
};
#endif
/*
rkcomponentmap.cpp - This file is part of RKWard (https://rkward.kde.org). Created: Thu May 12 2005
SPDX-FileCopyrightText: 2005-2015 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileCopyrightText: 2005-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -26,6 +26,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "../rkward.h"
#include "../settings/rksettingsmoduleplugins.h"
#include "../rbackend/rksessionvars.h"
#include "../misc/rkparsedversion.h"
#include "../dialogs/rkerrordialog.h"
QString RKPluginMapFile::makeFileName (const QString &filename) const {
......@@ -409,15 +410,12 @@ RKComponentHandle* RKComponentMap::getComponentHandleLocal (const QString &id) {
RK_DEBUG (PLUGIN, DL_INFO, "Looking for latest version of component %s, among %d candidates", qPrintable (id), candidates.size ());
RKComponentHandle* candidate = candidates.first ();
QString sufa;
quint32 vera = RKSessionVars::parseVersionString (candidate->getAboutData ().version, &sufa);
auto vera = RKParsedVersion(candidate->getAboutData().version);
for (int i = 1; i < candidates.size (); ++i) {
QString sufb;
quint32 verb = RKSessionVars::parseVersionString (candidates[i]->getAboutData ().version, &sufb);
if ((verb > vera) || ((verb == vera) && (sufb > sufa))) {
auto verb = RKParsedVersion(candidates[i]->getAboutData ().version);
if (verb > vera) {
candidate = candidates[i];
vera = verb;
sufa = sufb;
}
}
// purge inferior components to avoid future version lookups
......
/*
rkcomponentmeta - This file is part of RKWard (https://rkward.kde.org). Created: Wed Jan 09 2013
SPDX-FileCopyrightText: 2013-2014 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileCopyrightText: 2013-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -143,9 +143,9 @@ QList <RKComponentDependency> RKComponentDependency::parseDependencies (const QD
// Check for R dependency, first.
dep.type = RKComponentDependency::RBaseInstallation;
if (e.hasAttribute (R_min_version_tag)) dep.min_version = RKSessionVars::parseVersionString (e.attribute (R_min_version_tag), 0);
if (e.hasAttribute (R_max_version_tag)) dep.max_version = RKSessionVars::parseVersionString (e.attribute (R_max_version_tag), 0);
if ((dep.min_version > 0) || (dep.max_version < 0xFFFFFFFF)) ret.append (dep);
if (e.hasAttribute(R_min_version_tag)) dep.min_version = RKParsedVersion(e.attribute(R_min_version_tag));
if (e.hasAttribute(R_max_version_tag)) dep.max_version = RKParsedVersion(e.attribute(R_max_version_tag));
if (!(dep.min_version.isNull() && dep.max_version.isNull())) ret.append(dep);
XMLChildList deps = xml.getChildElements (e, QString (), DL_INFO);
for (int i = 0; i < deps.size (); ++i) {
......@@ -162,39 +162,22 @@ QList <RKComponentDependency> RKComponentDependency::parseDependencies (const QD
}
dep.package = xml.getStringAttribute (dep_e, "name", QString (), DL_ERROR);
dep.min_version = 0;
dep.max_version = 0xFFFFFFFF;
if (e.hasAttribute (any_min_version_tag)) dep.min_version = RKSessionVars::parseVersionString (e.attribute (any_min_version_tag), 0);
if (e.hasAttribute (any_max_version_tag)) dep.max_version = RKSessionVars::parseVersionString (e.attribute (any_max_version_tag), 0);
if (e.hasAttribute(any_min_version_tag)) dep.min_version = RKParsedVersion(e.attribute(any_min_version_tag));
if (e.hasAttribute(any_max_version_tag)) dep.max_version = RKParsedVersion(e.attribute(any_max_version_tag));
ret.append (dep);
}
// Add RKWard dependency, last
dep.type = RKComponentDependency::RKWardVersion;
dep.min_version = 0;
dep.max_version = 0xFFFFFFFF;
dep.source_info.clear ();
// Although we ignore it, here, RKWard dependencies may come with a non-numeric suffix
QString suffix_dummy;
if (e.hasAttribute (rkward_min_version_tag)) dep.min_version = RKSessionVars::parseVersionString (e.attribute (rkward_min_version_tag), &suffix_dummy);
if (e.hasAttribute (rkward_max_version_tag)) dep.max_version = RKSessionVars::parseVersionString (e.attribute (rkward_max_version_tag), &suffix_dummy);
if ((dep.min_version > 0) || (dep.max_version < 0xFFFFFFFF)) ret.append (dep);
if (e.hasAttribute(rkward_min_version_tag)) dep.min_version = RKParsedVersion(e.attribute(rkward_min_version_tag));
if (e.hasAttribute(rkward_max_version_tag)) dep.max_version = RKParsedVersion(e.attribute(rkward_max_version_tag));
if (!(dep.min_version.isNull() && dep.max_version.isNull())) ret.append(dep);
return ret;
}
QString numericVersionToString (quint32 numeric) {
QString ret;
for (int i = 3; i >= 0; --i) {
int ver_part = (numeric >> (i * 8)) & 0x000000FF;
ret.append (QString::number (ver_part));
if (i > 0) ret.append ('.');
}
if (ret.endsWith (QLatin1String (".0"))) ret.chop (2); // HACK: Don't print more than three version parts, unless the fourth is non-zero
return ret;
}
QString RKComponentDependency::depsToHtml (const QList <RKComponentDependency>& deps) {
RK_TRACE (PLUGIN);
......@@ -215,8 +198,8 @@ QString RKComponentDependency::depsToHtml (const QList <RKComponentDependency>&
ret.append (" \"" + dep.package + "\"");
if (!dep.source_info.isEmpty ()) ret.append (" (" + dep.source_info + ')');
}
if (dep.min_version > 0) ret.append (" &gt;= " + numericVersionToString (dep.min_version));
if (dep.max_version < 0xFFFFFFFF) ret.append (" &lt;= " + numericVersionToString (dep.max_version));
if (!dep.min_version.isNull()) ret.append(" &gt;= " + dep.min_version.toString());
if (!dep.max_version.isNull()) ret.append(" &lt;= " + dep.max_version.toString());
ret.append ("</li>");
}
ret.append ("</ul>");
......
......@@ -11,9 +11,11 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include <QDomElement>
#include <QList>
#include "../misc/rkparsedversion.h"
class XMLHelper;
struct RKComponentDependency {
RKComponentDependency () : type (RBaseInstallation), min_version (0), max_version (0xFFFFFFFF) {};
RKComponentDependency () : type(RBaseInstallation), min_version(RKParsedVersion()), max_version(RKParsedVersion::maxVersion()) {};
QString toHtml () const;
static QString depsToHtml (const QList<RKComponentDependency> &deps);
enum DependencyType {
......@@ -25,8 +27,8 @@ struct RKComponentDependency {
DependencyType type;
QString package;
QString source_info;
quint32 min_version;
quint32 max_version;
RKParsedVersion min_version;
RKParsedVersion max_version;
static QList<RKComponentDependency> parseDependencies (const QDomElement &e, XMLHelper &xml);
static bool isRKWardVersionCompatible (const QDomElement &e);
......
......@@ -24,9 +24,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "../debug.h"
RKSessionVars* RKSessionVars::_instance = 0;
quint32 RKSessionVars::rkward_version = 0;
QString RKSessionVars::rkward_version_suffix;
quint32 RKSessionVars::r_version = 0;
RKParsedVersion RKSessionVars::rkward_version(RKWARD_VERSION);
RKParsedVersion RKSessionVars::r_version;
QString RKSessionVars::r_version_string;
QString RKSessionVars::r_binary;
......@@ -55,7 +54,7 @@ void RKSessionVars::setRVersion (const QString& version_string) {
RK_DEBUG (RBACKEND, DL_WARNING, "R version has changed during runtime, from %s to %s", qPrintable (r_version_string), qPrintable (version_string));
}
r_version_string = version_string;
r_version = parseVersionString (version_string, 0);
r_version = RKParsedVersion(version_string);
}
QString RKSessionVars::RVersion(bool abbridged) {
......@@ -63,56 +62,18 @@ QString RKSessionVars::RVersion(bool abbridged) {
return r_version_string.section ('.', 0, 1);
}
quint32 RKSessionVars::parseVersionString (const QString &version, QString *suffix) {
quint32 ret = 0;
int pos = -1;
int opos = 0;
for (int i = 3; i >= 0; --i) {
while (true) {
++pos;
if (!(pos < version.size () && version[pos].isDigit ())) {
int val = version.midRef(opos, pos - opos).toInt();
if ((val < 0) || (val > 255) || (pos == opos)) {
RK_DEBUG (MISC, DL_ERROR, "Invalid version specification '%s'", qPrintable (version));
if (val > 255) val = 255;
else val = 0;
}
ret += val << (8 * i);
if ((pos < version.size ()) && (version[pos] == '.')) {
opos = pos + 1;
break;
}
opos = pos;
i = -1;
break;
}
}
}
if (opos <= (version.size () - 1)) {
if (suffix) *suffix = version.mid (opos);
else RK_DEBUG (MISC, DL_WARNING, "Non numeric portion ('%s') of version specification '%s' will be ignored.", qPrintable (version.mid (opos)), qPrintable (version));
}
return ret;
}
int RKSessionVars::compareRKWardVersion (const QString& version) {
if (!rkward_version) {
rkward_version = parseVersionString (RKWARD_VERSION, &rkward_version_suffix);
}
QString suffix;
quint32 ver = parseVersionString (version, &suffix);
if (ver < rkward_version) return -1;
auto ver = RKParsedVersion(version);
if (rkward_version > ver) return -1;
if (ver > rkward_version) return 1;
return (suffix.compare (rkward_version_suffix));
return 0;
}
int RKSessionVars::compareRVersion (const QString& version) {
if (r_version_string.isEmpty()) return 0;
quint32 ver = parseVersionString (version, 0);
if (ver < r_version) return -1;
auto ver = RKParsedVersion(version);
if (r_version > ver) return -1;
if (ver > r_version) return 1;
return 0;
}
......
......@@ -11,6 +11,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include <QObject>
#include <QStringList>
#include "../misc/rkparsedversion.h"
class RInterface;
/** Singleton for storing information about the running R session, and - for some of the info - notifying about changes. */
......@@ -31,7 +33,6 @@ public:
/** Split "a.b.c.d.e-fghi" into up to four numeric portions (returned as four bytes in a single 32bit unsigned int).
Anything else (everything after the fourth dot, or after the first character that is neither dot, nor digit)
is returned as suffix (via the suffix pointer; if that is 0, an error is reported, instead). */
static quint32 parseVersionString (const QString &version, QString *suffix);
static QStringList frontendSessionInfo ();
static QString RBinary() { return r_binary; }
signals:
......@@ -44,9 +45,8 @@ private:
static RKSessionVars* _instance;
QStringList installed_packages;
static quint32 rkward_version;
static QString rkward_version_suffix;
static quint32 r_version;
static RKParsedVersion rkward_version;
static RKParsedVersion r_version;
static QString r_version_string;
friend int main(int, char**);
static QString r_binary;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment