[education/rkward] rkward: Refactor version parsing
Thomas Friedrichsmeier
null at kde.org
Sat May 7 12:41:37 BST 2022
Git commit 033b7806b8ce258d04cdd7f6b27577eaf594586c by Thomas Friedrichsmeier.
Committed on 07/05/2022 at 11:41.
Pushed by tfry into branch 'master'.
Refactor version parsing
M +1 -0 rkward/misc/CMakeLists.txt
A +53 -0 rkward/misc/rkparsedversion.cpp [License: GPL(v2.0+)]
A +40 -0 rkward/misc/rkparsedversion.h [License: GPL(v2.0+)]
M +5 -7 rkward/plugin/rkcomponentmap.cpp
M +11 -28 rkward/plugin/rkcomponentmeta.cpp
M +5 -3 rkward/plugin/rkcomponentmeta.h
M +8 -47 rkward/rbackend/rksessionvars.cpp
M +4 -4 rkward/rbackend/rksessionvars.h
https://invent.kde.org/education/rkward/commit/033b7806b8ce258d04cdd7f6b27577eaf594586c
diff --git a/rkward/misc/CMakeLists.txt b/rkward/misc/CMakeLists.txt
index 55309b54..81a4ed09 100644
--- a/rkward/misc/CMakeLists.txt
+++ b/rkward/misc/CMakeLists.txt
@@ -29,6 +29,7 @@ SET(misc_STAT_SRCS
rkdialogbuttonbox.cpp
rkoutputdirectory.cpp
rkstyle.cpp
+ rkparsedversion.cpp
)
ADD_LIBRARY(misc STATIC ${misc_STAT_SRCS})
diff --git a/rkward/misc/rkparsedversion.cpp b/rkward/misc/rkparsedversion.cpp
new file mode 100644
index 00000000..88c34709
--- /dev/null
+++ b/rkward/misc/rkparsedversion.cpp
@@ -0,0 +1,53 @@
+/*
+rkparsedversion - This file is part of the RKWard project. Created: Sat May 07 2022
+SPDX-FileCopyrightText: 2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileContributor: The RKWard Team <rkward-devel at 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;
+}
diff --git a/rkward/misc/rkparsedversion.h b/rkward/misc/rkparsedversion.h
new file mode 100644
index 00000000..3d105e9f
--- /dev/null
+++ b/rkward/misc/rkparsedversion.h
@@ -0,0 +1,40 @@
+/*
+rkparsedversion - This file is part of the RKWard project. Created: Sat May 07 2022
+SPDX-FileCopyrightText: 2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileContributor: The RKWard Team <rkward-devel at 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
diff --git a/rkward/plugin/rkcomponentmap.cpp b/rkward/plugin/rkcomponentmap.cpp
index b4e015b4..96d70e2a 100644
--- a/rkward/plugin/rkcomponentmap.cpp
+++ b/rkward/plugin/rkcomponentmap.cpp
@@ -1,6 +1,6 @@
/*
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 at kdemail.net>
+SPDX-FileCopyrightText: 2005-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel at 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
diff --git a/rkward/plugin/rkcomponentmeta.cpp b/rkward/plugin/rkcomponentmeta.cpp
index c9025d12..4ea4963f 100644
--- a/rkward/plugin/rkcomponentmeta.cpp
+++ b/rkward/plugin/rkcomponentmeta.cpp
@@ -1,6 +1,6 @@
/*
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 at kdemail.net>
+SPDX-FileCopyrightText: 2013-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel at 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 (" >= " + numericVersionToString (dep.min_version));
- if (dep.max_version < 0xFFFFFFFF) ret.append (" <= " + numericVersionToString (dep.max_version));
+ if (!dep.min_version.isNull()) ret.append(" >= " + dep.min_version.toString());
+ if (!dep.max_version.isNull()) ret.append(" <= " + dep.max_version.toString());
ret.append ("</li>");
}
ret.append ("</ul>");
diff --git a/rkward/plugin/rkcomponentmeta.h b/rkward/plugin/rkcomponentmeta.h
index 588b555f..12c9c53c 100644
--- a/rkward/plugin/rkcomponentmeta.h
+++ b/rkward/plugin/rkcomponentmeta.h
@@ -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);
diff --git a/rkward/rbackend/rksessionvars.cpp b/rkward/rbackend/rksessionvars.cpp
index f9fe68f5..da6b495c 100644
--- a/rkward/rbackend/rksessionvars.cpp
+++ b/rkward/rbackend/rksessionvars.cpp
@@ -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;
}
diff --git a/rkward/rbackend/rksessionvars.h b/rkward/rbackend/rksessionvars.h
index ad7c1052..56ec9580 100644
--- a/rkward/rbackend/rksessionvars.h
+++ b/rkward/rbackend/rksessionvars.h
@@ -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;
More information about the rkward-tracker
mailing list