[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