[rkward-cvs] [rkward] /: Allow to override plugins (based on the specified version), e.g. for updating an "official" plugin by way of an external pluginmap.

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Fri Dec 19 20:33:24 UTC 2014


Git commit f68a324c88b1e7e2bc9c0a7060b997110dc78758 by Thomas Friedrichsmeier.
Committed on 19/12/2014 at 20:25.
Pushed by tfry into branch 'master'.

Allow to override plugins (based on the specified version), e.g. for updating an "official" plugin by way of an external pluginmap.
- More than one plugin can have the same id
- The plugin to use will be picked after all pluginmaps are loaded.

M  +1    -0    ChangeLog
M  +80   -22   rkward/plugin/rkcomponentmap.cpp
M  +18   -11   rkward/plugin/rkcomponentmap.h
M  +1    -0    rkward/plugin/rkcomponentmeta.cpp
M  +2    -0    rkward/plugin/rkcomponentmeta.h
M  +4    -9    rkward/windows/rkhtmlwindow.cpp

http://commits.kde.org/rkward/f68a324c88b1e7e2bc9c0a7060b997110dc78758

diff --git a/ChangeLog b/ChangeLog
index 282d6c7..ec078bd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,4 @@
+- Allow to override plugins from a different pluginmap (the plugin with the highest specified version is used)
 - Fixed: Error when using fix() or trace(...edit=TRUE) with default settings on some systems
 - When the RKWard installation has moved on disk, try to adjust stored .pluginmaps paths, accordingly
   - TODO: Actually test this on Windows
diff --git a/rkward/plugin/rkcomponentmap.cpp b/rkward/plugin/rkcomponentmap.cpp
index fd70859..da2b025 100644
--- a/rkward/plugin/rkcomponentmap.cpp
+++ b/rkward/plugin/rkcomponentmap.cpp
@@ -35,8 +35,9 @@
 #include "../rkglobals.h"
 #include "../rkward.h"
 #include "../settings/rksettingsmoduleplugins.h"
+#include "../rbackend/rksessionvars.h"
 
-QString RKPluginMapFile::makeFileName (const QString &filename) {
+QString RKPluginMapFile::makeFileName (const QString &filename) const {
 	return QDir::cleanPath (QDir (basedir).filePath (filename));
 }
 
@@ -46,6 +47,15 @@ QString RKPluginMapFile::parseId (const QDomElement& e, XMLHelper &xml) {
 	return (xml.getStringAttribute (e, "namespace", "rkward", DL_WARNING) + "::" + xml.getStringAttribute (e, "id", QString (), DL_INFO));
 }
 
+RKComponentAboutData RKPluginMapFile::getAboutData () {
+	RK_TRACE (PLUGIN);
+
+	if (about) return *about;
+	XMLHelper xml (filename);
+	QDomElement element = xml.openXMLFile (DL_ERROR);
+	about = new RKComponentAboutData (xml.getChildElement (element, "about", DL_INFO), xml);	// might be empty, but not null
+	return *about;
+}
 
 RKComponentGUIXML::RKComponentGUIXML () {
 	RK_TRACE (PLUGIN);
@@ -248,8 +258,8 @@ void RKComponentMap::clearAll () {
 	}
 	components.clear ();
 
-	for (PluginMapFileMap::const_iterator it = pluginmapfiles.constBegin (); it != pluginmapfiles.constEnd (); ++it) {
-		delete (it.value ());
+	for (int i = 0; i < pluginmapfiles.size (); ++i) {
+		delete (pluginmapfiles[i]);
 	}
 	pluginmapfiles.clear ();
 	component_attributes.clear ();
@@ -290,8 +300,29 @@ RKComponentHandle* RKComponentMap::getComponentHandle (const QString &id) {
 RKComponentHandle* RKComponentMap::getComponentHandleLocal (const QString &id) {
 	RK_TRACE (PLUGIN);
 
-	if (components.contains (id)) return (components[id]);
-	return 0;
+	QList<RKComponentHandle*> candidates = components.values (id);
+	if (candidates.isEmpty ()) return 0;
+	if (candidates.length () == 1) return candidates.first ();
+
+	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);
+	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))) {
+			candidate = candidates[i];
+			vera = verb;
+			sufa = sufb;
+		}
+	}
+	// purge inferior components to avoid future version lookups
+	RK_DEBUG (PLUGIN, DL_INFO, "Latest version is '%s'. Forgetting about the others.", qPrintable (candidate->getFilename ()));
+	components.remove (id);
+	components.insert (id, candidate);
+
+	return candidate;
 }
 
 //static
@@ -304,14 +335,18 @@ QString RKComponentMap::getComponentId (RKComponentHandle* by_component) {
 QString RKComponentMap::getComponentIdLocal (RKComponentHandle* component) {
 	RK_TRACE (PLUGIN);
 
-	for (ComponentMap::iterator it = components.begin (); it != components.end (); ++it) {
-		if (it.value () == component) {
-			return it.key ();
-		}
-	}
+	QString component_found = components.key (component, QString ());
+	RK_ASSERT (!component_found.isNull ());
+	return (component_found);
+}
 
-	RK_ASSERT (false);
-	return (QString ());
+bool RKComponentMap::isPluginMapLoaded (const QString& abs_filename) const {
+	RK_TRACE (PLUGIN);
+
+	for (int i = 0; i < pluginmapfiles.size (); ++i) {
+		if (pluginmapfiles[i]->filename == abs_filename) return true;
+	}
+	return false;
 }
 
 //static
@@ -414,7 +449,7 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
 	RKPluginMapParseResult ret;
 
 	QString plugin_map_file_abs = QFileInfo (plugin_map_file).absoluteFilePath ();
-	if (pluginmapfiles.contains (plugin_map_file_abs)) {
+	if (isPluginMapLoaded (plugin_map_file_abs)) {
 		RK_DEBUG (PLUGIN, DL_INFO, "Plugin map file '%s' already loaded", plugin_map_file.toLatin1().data ());
 		return ret;
 	}
@@ -432,9 +467,9 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
 	QString prefix = QFileInfo (plugin_map_file_abs).absolutePath() + '/' + xml.getStringAttribute (document_element, "base_prefix", QString::null, DL_INFO);
 	QString cnamespace = xml.getStringAttribute (document_element, "namespace", "rkward", DL_INFO) + "::";
 
-	RKPluginMapFile *pluginmap_file_desc = new RKPluginMapFile (prefix, xml.messageCatalog ());
+	RKPluginMapFile* pluginmap_file_desc = new RKPluginMapFile (QFileInfo (plugin_map_file).absoluteFilePath (), prefix, xml.messageCatalog ());
 	pluginmap_file_desc->id = RKPluginMapFile::parseId (document_element, xml);
-	pluginmapfiles.insert (QFileInfo (plugin_map_file).absoluteFilePath (), pluginmap_file_desc);
+	pluginmapfiles.append (pluginmap_file_desc);
 
 	// step 0: check dependencies, parse about, and initialize
 	QDomElement dependencies = xml.getChildElement (document_element, "dependencies", DL_INFO);
@@ -461,9 +496,9 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
 			QString map_id = xml.getStringAttribute (*it, "map", QString (), DL_ERROR);
 			// Try to locate the map among the already loaded files, first
 			QString file;
-			for (PluginMapFileMap::const_iterator pmit = pluginmapfiles.constBegin (); pmit != pluginmapfiles.constEnd (); ++pmit) {
-				if (pmit.value ()->id == map_id) {
-					file = pmit.key ();
+			for (int i = 0; i < pluginmapfiles.size (); ++i) {
+				if (pluginmapfiles[i]->id == map_id) {
+					file = pluginmapfiles[i]->filename;
 					break;
 				}
 			}
@@ -506,9 +541,13 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
 		int type = xml.getMultiChoiceAttribute ((*it), "type", "standard", 0, DL_WARNING);
 		QString label = xml.i18nStringAttribute ((*it), "label", i18n ("(no label)"), DL_WARNING);
 
+		if (local_components.contains (id)) {
+			ret.addAndPrintError (DL_WARNING, i18n ("Duplicate declaration of component id \"%1\" within pluginmap file \"%2\".", id, plugin_map_file_abs));
+		}
 		if (components.contains (id)) {
-			ret.addAndPrintError (DL_WARNING, i18n ("RKComponentMap already contains a component with id \"%1\". Ignoring second entry.", id));
-		} else if (!QFileInfo (pluginmap_file_desc->makeFileName (filename)).isReadable ()) {
+			RK_DEBUG (PLUGIN, DL_INFO, "RKComponentMap already contains a component with id \"%1\". Highest version will be picked at runtime.", qPrintable (id));
+		}
+		if (!QFileInfo (pluginmap_file_desc->makeFileName (filename)).isReadable ()) {
 			ret.addAndPrintError (DL_ERROR, i18n ("Specified file '%1' for component id \"%2\" does not exist or is not readable. Ignoring.", filename, id));
 		} else {
 			// create and initialize component handle
@@ -647,10 +686,29 @@ QList <RKComponentDependency> RKComponentHandle::getDependencies () {
 	return (ret + (*it));
 }
 
-QString RKComponentHandle::getPluginmapFilename () {
+QString RKComponentHandle::getPluginmapFilename () const {
+	RK_TRACE (PLUGIN);
+
+	if (!plugin_map) {
+		RK_ASSERT (plugin_map);
+		return QString ();
+	}
+	return plugin_map->getFileName ();
+}
+
+RKComponentAboutData RKComponentHandle::getAboutData () {
 	RK_TRACE (PLUGIN);
 
-	return RKComponentMap::getMap ()->pluginmapfiles.key (plugin_map);
+	// NOTE: In order to determine the message catalog to use, we have to open the pluginmap file...
+	XMLHelper pluginmap_xml (getPluginmapFilename ());
+	QDomElement pluginmap_doc = pluginmap_xml.openXMLFile (DL_ERROR);
+
+	XMLHelper component_xml (getFilename (), pluginmap_xml.messageCatalog ());
+	QDomElement component_doc = component_xml.openXMLFile (DL_ERROR);
+	QDomElement about = component_xml.getChildElement (component_doc, "about", DL_INFO);
+	if (!about.isNull ()) return RKComponentAboutData (about, component_xml);
+
+	return (plugin_map->getAboutData ());
 }
 
 ///########################### END RKComponentHandle ###############################
diff --git a/rkward/plugin/rkcomponentmap.h b/rkward/plugin/rkcomponentmap.h
index ab2054d..108ae75 100644
--- a/rkward/plugin/rkcomponentmap.h
+++ b/rkward/plugin/rkcomponentmap.h
@@ -27,20 +27,25 @@ class XMLHelper;
 /** very simple helper class to keep track of .pluginmap files */
 class RKPluginMapFile {
 public:
-	RKPluginMapFile (const QString &basedir, const RKMessageCatalog *_catalog) { RKPluginMapFile::basedir = basedir; catalog = _catalog; };
-	~RKPluginMapFile () {};
+	RKPluginMapFile (const QString &filename, const QString &basedir, const RKMessageCatalog *_catalog) { RKPluginMapFile::filename = filename; RKPluginMapFile::basedir = basedir; catalog = _catalog; about = 0; };
+	~RKPluginMapFile () { delete about; };
 
-	QString getBaseDir () { return basedir; };
-	QString makeFileName (const QString &filename);
-	QList<RKComponentDependency> getDependencies () { return dependencies; };
+	QString getBaseDir () const { return basedir; };
+	QString getFileName () const { return filename; };
+	QString makeFileName (const QString &filename) const;
+	QList<RKComponentDependency> getDependencies () const { return dependencies; };
 	static QString parseId (const QDomElement &e, XMLHelper &xml);
 	const RKMessageCatalog *messageCatalog () const { return catalog; };
+	// Get the about data for this pluginmap.
+	RKComponentAboutData getAboutData ();
 private:
 friend class RKComponentMap;
 	QString basedir;
+	QString filename;
 	QString id;
 	QList<RKComponentDependency> dependencies;
 	const RKMessageCatalog *catalog;
+	RKComponentAboutData *about;
 };
 
 /** enum of different types of RKComponent */
@@ -72,7 +77,7 @@ public:
 	QString getLabel () { return label; };
 	RKComponentType getType () { return type; };
 	bool isPlugin ();
-	QString getPluginmapFilename ();
+	QString getPluginmapFilename () const;
 
 	RKStandardComponent *invoke (RKComponent *parent_component, QWidget *parent_widget);
 
@@ -88,6 +93,9 @@ public:
 /** Returns whether this component is accessible from the menu, somewhere (else it might be in a context) */
 	bool isAccessible () const { return is_accessible; };
 	const RKMessageCatalog *messageCatalog () const { return plugin_map->messageCatalog (); };
+/** Returns the about data for this component (likely that of the containing pluginmap). Note that in contrast to RKPluginMapFile::getAboutData (), the parsed
+ *  about data is not cached, as access to this data should be infrequent. */
+	RKComponentAboutData getAboutData ();
 protected:
 /** The plugin map where this component was declared */
 	RKPluginMapFile *plugin_map;
@@ -99,7 +107,7 @@ private:
 	bool is_accessible;
 };
 
-#include <qmap.h>
+#include <QMultiMap>
 #include <QDomDocument>
 
 #include <kxmlguiclient.h>
@@ -207,13 +215,13 @@ public:
 	static bool invokeComponent (const QString &component_id, const QStringList &serialized_settings, ComponentInvocationMode submit_mode = ManualSubmit, QString *message=0, RCommandChain *in_chain = 0);
 /** @returns a list of all currently registered component ids */
 	QStringList allComponentIds () { return components.keys(); };
-	bool isPluginMapLoaded (const QString& abs_filename) { return pluginmapfiles.contains (abs_filename); };
+	bool isPluginMapLoaded (const QString& abs_filename) const;
 public slots:
 /** Slot called, when a menu-item for a component is selected. Responsible for creating the GUI. */
 	void activateComponent ();
 private:
 /** typedef for easy reference to iterator */
-	typedef QMap<QString, RKComponentHandle*> ComponentMap;
+	typedef QMultiMap<QString, RKComponentHandle*> ComponentMap;
 /** the actual map of components */
 	ComponentMap components;
 
@@ -224,8 +232,7 @@ private:
 	typedef QMap<QString, RKContextMap*> RKComponentContextMap;
 	RKComponentContextMap contexts;
 
-	typedef QMap<QString, RKPluginMapFile*> PluginMapFileMap;
-	PluginMapFileMap pluginmapfiles;
+	QList<RKPluginMapFile*> pluginmapfiles;
 
 	static RKComponentMap *component_map;
 friend class RKComponentHandle;
diff --git a/rkward/plugin/rkcomponentmeta.cpp b/rkward/plugin/rkcomponentmeta.cpp
index a39cb15..90b0f46 100644
--- a/rkward/plugin/rkcomponentmeta.cpp
+++ b/rkward/plugin/rkcomponentmeta.cpp
@@ -36,6 +36,7 @@ RKComponentAboutData::RKComponentAboutData (const QDomElement& e, XMLHelper &xml
 	RK_TRACE (PLUGIN);
 	if (e.isNull ()) return;
 
+	valid = true;
 	name = xml.i18nStringAttribute (e, "name", QString (), DL_INFO);
 	version = xml.getStringAttribute (e, "version", QString (), DL_INFO);
 	releasedate = xml.getStringAttribute (e, "releasedate", QString (), DL_INFO);
diff --git a/rkward/plugin/rkcomponentmeta.h b/rkward/plugin/rkcomponentmeta.h
index fc13adc..991560d 100644
--- a/rkward/plugin/rkcomponentmeta.h
+++ b/rkward/plugin/rkcomponentmeta.h
@@ -52,6 +52,7 @@ struct RKComponentAuthor {
 
 class RKComponentAboutData {
 public:
+	RKComponentAboutData () { valid = false; };
 	RKComponentAboutData (const QDomElement &e, XMLHelper &xml);
 	~RKComponentAboutData ();
 	QString toHtml () const;
@@ -67,6 +68,7 @@ public:
 	QList<RKComponentAuthor> authors;
 	QString translator_names;
 	QString translator_emails;
+	bool valid;
 };
 
 #endif
diff --git a/rkward/windows/rkhtmlwindow.cpp b/rkward/windows/rkhtmlwindow.cpp
index c85d3c1..cea48e0 100644
--- a/rkward/windows/rkhtmlwindow.cpp
+++ b/rkward/windows/rkhtmlwindow.cpp
@@ -645,18 +645,13 @@ bool RKHTMLWindow::renderRKHelp (const KUrl &url) {
 	}
 
 	// "about" section
+	RKComponentAboutData about;
 	if (for_component) {
-		element = component_xml.getChildElement (component_doc_element, "about", DL_INFO);
-		if (element.isNull ()) {
-			XMLHelper pluginmap_helper (chandle->getPluginmapFilename (), chandle->messageCatalog ());
-			element = pluginmap_helper.openXMLFile (DL_ERROR);
-			element = pluginmap_helper.getChildElement (element, "about", DL_INFO);
-		}
+		about = chandle->getAboutData ();
 	} else {
-		element = help_xml.getChildElement (help_doc_element, "about", DL_INFO);
+		about = RKComponentAboutData (help_xml.getChildElement (help_doc_element, "about", DL_INFO), help_xml);
 	}
-	if (!element.isNull ()) {
-		RKComponentAboutData about (element, for_component ? component_xml : help_xml);
+	if (about.valid) {
 		writeHTML (startSection ("about", i18n ("About"), QString (), &anchors, &anchornames));
 		writeHTML (about.toHtml ());
 	}





More information about the rkward-tracker mailing list