From 8cb8790aab830dd91f7d6d3a549e5590c7c22d42 Mon Sep 17 00:00:00 2001 From: mpv Date: Fri, 20 Oct 2017 19:20:57 +0300 Subject: [PATCH] Support of additional environment-defined path to plugins. Also make plugins.xml appeared twice works as extension. --- src/Config/CMakeLists.txt | 2 + src/Config/Config_FeatureMessage.h | 4 +- src/Config/Config_Keywords.h | 1 + src/Config/Config_ModuleReader.cpp | 14 +++ src/Config/Config_PluginMessage.cpp | 48 ++++++++++ src/Config/Config_PluginMessage.h | 64 +++++++++++++ src/Config/Config_XMLReader.cpp | 109 +++++++++++++++++----- src/Config/Config_XMLReader.h | 12 ++- src/Model/Model_Session.cpp | 57 ++++++++--- src/Model/Model_Session.h | 5 + src/ModuleBase/ModuleBase_IconFactory.cpp | 14 ++- 11 files changed, 278 insertions(+), 52 deletions(-) create mode 100644 src/Config/Config_PluginMessage.cpp create mode 100644 src/Config/Config_PluginMessage.h diff --git a/src/Config/CMakeLists.txt b/src/Config/CMakeLists.txt index d1a1781ab..e120e21b8 100644 --- a/src/Config/CMakeLists.txt +++ b/src/Config/CMakeLists.txt @@ -29,6 +29,7 @@ INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/src/Events SET(PROJECT_HEADERS Config_def.h Config_FeatureMessage.h + Config_PluginMessage.h Config_XMLReader.h Config_ModuleReader.h Config_FeatureReader.h @@ -48,6 +49,7 @@ SET(PROJECT_HEADERS SET(PROJECT_SOURCES Config_FeatureMessage.cpp + Config_PluginMessage.cpp Config_XMLReader.cpp Config_ModuleReader.cpp Config_FeatureReader.cpp diff --git a/src/Config/Config_FeatureMessage.h b/src/Config/Config_FeatureMessage.h index 86d308684..a4c610551 100644 --- a/src/Config/Config_FeatureMessage.h +++ b/src/Config/Config_FeatureMessage.h @@ -18,8 +18,8 @@ // email : webmaster.salome@opencascade.com // -#ifndef CONFIG_MESSAGE_H -#define CONFIG_MESSAGE_H +#ifndef CONFIG_FEATUREMESSAGE_H +#define CONFIG_FEATUREMESSAGE_H #include #include diff --git a/src/Config/Config_Keywords.h b/src/Config/Config_Keywords.h index 81a8d8dea..95a54bb42 100644 --- a/src/Config/Config_Keywords.h +++ b/src/Config/Config_Keywords.h @@ -123,6 +123,7 @@ const static char* PLUGIN_CONFIG = "configuration"; const static char* PLUGIN_LIBRARY = "library"; const static char* PLUGIN_SCRIPT = "script"; const static char* PLUGIN_DEPENDENCY = "dependency"; +const static char* PLUGIN_USES = "uses"; /* * Hardcoded xml entities of dataModel.xml diff --git a/src/Config/Config_ModuleReader.cpp b/src/Config/Config_ModuleReader.cpp index 168983a5e..67b4ee990 100644 --- a/src/Config/Config_ModuleReader.cpp +++ b/src/Config/Config_ModuleReader.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,13 @@ void Config_ModuleReader::processNode(xmlNodePtr theNode) std::string aPluginLibrary = getProperty(theNode, PLUGIN_LIBRARY); std::string aPluginScript = getProperty(theNode, PLUGIN_SCRIPT); std::string aPluginName = addPlugin(aPluginLibrary, aPluginScript, aPluginConf); + std::string aUsesPlugin = getProperty(theNode, PLUGIN_USES); + if (!aUsesPlugin.empty()) { // send information about hte plugin dependencies + std::shared_ptr aMess(new Config_PluginMessage( + Events_Loop::loop()->eventByName(Config_PluginMessage::EVENT_ID()), aPluginName)); + aMess->setUses(aUsesPlugin); + Events_Loop::loop()->send(aMess); + } std::list aFeatures = importPlugin(aPluginName, aPluginConf); std::list::iterator it = aFeatures.begin(); @@ -167,6 +175,12 @@ std::string Config_ModuleReader::addPlugin(const std::string& aPluginLibrary, void Config_ModuleReader::loadPlugin(const std::string& thePluginName) { + // informs model that plugin loading is started + static const Events_ID kEVENT_ID = + Events_Loop::loop()->eventByName(Config_PluginMessage::EVENT_ID()); + std::shared_ptr aMess(new Config_PluginMessage(kEVENT_ID, thePluginName)); + Events_Loop::loop()->send(aMess); + PluginType aType = Config_ModuleReader::Binary; if(myPluginTypes.find(thePluginName) != myPluginTypes.end()) { aType = myPluginTypes.at(thePluginName); diff --git a/src/Config/Config_PluginMessage.cpp b/src/Config/Config_PluginMessage.cpp new file mode 100644 index 000000000..eaee691a6 --- /dev/null +++ b/src/Config/Config_PluginMessage.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// + +#include "Config_PluginMessage.h" + +Config_PluginMessage::Config_PluginMessage(const Events_ID theId, + const std::string& thePluginId, const void* theParent) + : Events_Message(theId, theParent) +{ + myPluginId = thePluginId; +} + +Config_PluginMessage::~Config_PluginMessage() +{ + +} + +const std::string& Config_PluginMessage::pluginId() const +{ + return myPluginId; +} + +const std::string& Config_PluginMessage::uses() const +{ + return myUses; +} + +void Config_PluginMessage::setUses(const std::string& theUses) +{ + myUses = theUses; +} diff --git a/src/Config/Config_PluginMessage.h b/src/Config/Config_PluginMessage.h new file mode 100644 index 000000000..8778c3d59 --- /dev/null +++ b/src/Config/Config_PluginMessage.h @@ -0,0 +1,64 @@ +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// + +#ifndef CONFIG_PLUGINMESSAGE_H +#define CONFIG_PLUGINMESSAGE_H + +#include +#include + +#include + +/*! + * \class Config_PluginMessage + * \ingroup Config + * \brief Information that plugin is started to load. + * + * Contains the plugin name. Event is posted just before load of the plugin + * before registerPlugin call. + */ +class Config_PluginMessage : public Events_Message +{ + std::string myPluginId; ///< Plugin unique id + std::string myUses; ///< Which plugins are used by this one Id + + public: + /// Event ID that plugin will be loaded + inline static const char* EVENT_ID() + { + static const char * MY_EVENT_ID("BeforePluginLoad"); + return MY_EVENT_ID; + } + + /// Constructs Config_PluginMessage + CONFIG_EXPORT Config_PluginMessage(const Events_ID theId, + const std::string& thePluginId, const void* theParent = 0); + /// Deletes Config_PluginMessage + CONFIG_EXPORT virtual ~Config_PluginMessage(); + + /// Plugin Id + CONFIG_EXPORT const std::string& pluginId() const; + /// Which plugins are used by this one Id + CONFIG_EXPORT const std::string& uses() const; + /// Sets which plugins are used by this one Id + CONFIG_EXPORT void setUses(const std::string& theUses); +}; + +#endif // CONFIG_MESSAGE_H diff --git a/src/Config/Config_XMLReader.cpp b/src/Config/Config_XMLReader.cpp index 6b29a5851..0ab7ce325 100644 --- a/src/Config/Config_XMLReader.cpp +++ b/src/Config/Config_XMLReader.cpp @@ -30,6 +30,7 @@ #include #include +#include #ifdef WIN32 #pragma warning(disable : 4996) // for getenv @@ -46,26 +47,12 @@ #endif Config_XMLReader::Config_XMLReader(const std::string& theXmlFileName) - : myXmlDoc(NULL) + : myXmlDoc(NULL), myRootFileName(theXmlFileName) { - std::string prefix = ""; - Config_Prop* aProp = Config_PropManager::findProp("Plugins", "default_path"); - if (aProp) - prefix = aProp->value(); - /* - * Get path to *.xml files (typically ./bin/../plugins/) - - * the problem: application may be launched using python executable, - * to use environment variable (at least for the current moment) - */ - if (prefix.empty()) - prefix = pluginConfigFile(); - - myDocumentPath = prefix + FSEP + theXmlFileName; - std::ifstream aTestFile(myDocumentPath); - if (!aTestFile) - Events_InfoMessage("Config_XMLReader", "Unable to open %1").arg(myDocumentPath).send(); - aTestFile.close(); + myDocumentPath = findConfigFile(theXmlFileName); + if (myDocumentPath.empty()) { + Events_InfoMessage("Config_XMLReader", "Unable to open %1").arg(theXmlFileName).send(); + } } Config_XMLReader::~Config_XMLReader() @@ -89,13 +76,84 @@ std::string Config_XMLReader::pluginConfigFile() return aValue; } +std::string Config_XMLReader::findConfigFile(const std::string theFileName, const int theFindIndex) +{ + int aResultIndex = 0; + for(int aSolution = 0; aSolution < 12; aSolution++) { + std::string aFileName; + if (aSolution == 0) { + Config_Prop* aProp = Config_PropManager::findProp("Plugins", "default_path"); + if (!aProp) + continue; + aFileName = aProp->value(); + } else { + std::ostringstream anEnvName; + if (aSolution == 1) + anEnvName<<"SHAPER_ROOT_DIR"; + else if (aSolution == 2) + anEnvName<<"OPENPARTS_ROOT_DIR"; + else + anEnvName<<"OPENPARTS_PLUGINS_DIR"; + + char* anEnv = getenv(anEnvName.str().c_str()); + if (!anEnv) + continue; + if (aSolution > 2) { // there may be several paths separated by ";" symbol + std::string anEnvPart = anEnv; + size_t aPosStart = 0, aPosEnd; + for(int aSubNum = 0; aSubNum < aSolution - 3; aSubNum++) { + aPosStart++; + aPosStart = anEnvPart.find(';', aPosStart); + if (aPosStart == std::string::npos) + break; + } + if (aPosStart == std::string::npos) + break; + if (aPosStart != 0) + aPosStart++; + aPosEnd = anEnvPart.find(';', aPosStart); + aFileName = anEnvPart.substr(aPosStart, + aPosEnd == std::string::npos ? aPosEnd : aPosEnd - aPosStart) + FSEP; + } else { + aFileName = std::string(anEnv) + FSEP; + } + if (aSolution == 1) + aFileName += std::string("share") + FSEP + "salome" + FSEP + "resources" + FSEP + "shaper"; + else if (aSolution == 2) + aFileName += "plugins"; + } + + aFileName += FSEP + theFileName; + std::ifstream aTestFile(aFileName); + if (aTestFile) { + if (aResultIndex == theFindIndex) + return aFileName; + aResultIndex++; + if (aSolution == 1) // don't allow SHAPER and OpenParts paths treated simultaneously + aSolution++; + } + } + return ""; // no files found +} + void Config_XMLReader::readAll() { - // to load external modules dependencies (like GEOm for Connector Feature + // to load external modules dependencies (like GEOM for Connector Feature) Config_ModuleReader::loadScript("salome.shaper.initConfig", false); - xmlNodePtr aRoot = findRoot(); - readRecursively(aRoot); + for(int aSolution = 0; true; aSolution++) { + std::string aFoundFile = findConfigFile(myRootFileName, aSolution); + if (aFoundFile.empty()) { + break; // no more solutions + } + + if (myXmlDoc != NULL) { // clear the previous XML document - now the new one will be opened + xmlFreeDoc(myXmlDoc); + myXmlDoc = NULL; + } + xmlNodePtr aRoot = findRoot(aFoundFile); + readRecursively(aRoot); + } } void Config_XMLReader::processNode(xmlNodePtr theNode) @@ -120,14 +178,15 @@ bool Config_XMLReader::processChildren(xmlNodePtr aNode) return true; } -xmlNodePtr Config_XMLReader::findRoot() +xmlNodePtr Config_XMLReader::findRoot(const std::string theDocumentPath) { + std::string aDocPath = theDocumentPath.empty() ? myDocumentPath : theDocumentPath; if (myXmlDoc == NULL) { - myXmlDoc = xmlParseFile(myDocumentPath.c_str()); + myXmlDoc = xmlParseFile(aDocPath.c_str()); } if (myXmlDoc == NULL) { #ifdef _DEBUG - std::cout << "Config_XMLReader::import: " << "Document " << myDocumentPath + std::cout << "Config_XMLReader::import: " << "Document " << aDocPath << " is not parsed successfully." << std::endl; #endif return NULL; diff --git a/src/Config/Config_XMLReader.h b/src/Config/Config_XMLReader.h index 53b07024f..6b4fde6ba 100644 --- a/src/Config/Config_XMLReader.h +++ b/src/Config/Config_XMLReader.h @@ -66,13 +66,18 @@ class Config_XMLReader */ CONFIG_EXPORT void readAll(); /*! - * Returns xmlNodePtr to the root of reader's document - * or NULL if not found + * Returns xmlNodePtr to the root of reader's document or NULL if not found. + * If the path to the document to read is empty, uses myDocumentPath. */ - CONFIG_EXPORT xmlNodePtr findRoot(); + CONFIG_EXPORT xmlNodePtr findRoot(const std::string theDocumentPath = ""); CONFIG_EXPORT const char* encoding() const; + /// Checks all possible paths to configuration file given + /// Uses theFindIndex if several solutions can be found (this is the number of solution to find) + CONFIG_EXPORT static std::string + findConfigFile(const std::string theFileName, const int theFindIndex = 0); + protected: /*! * \brief Allows to customize reader's behavior for a node. Virtual. @@ -117,6 +122,7 @@ class Config_XMLReader protected: std::string myDocumentPath; ///< Path to the xml document xmlDocPtr myXmlDoc; ///< Root of the xml document + std::string myRootFileName; ///< name of the root file /// A map to store all parent's attributes. /// The key has from "Node_Name:Node_Attribute" std::map myCachedAttributes; diff --git a/src/Model/Model_Session.cpp b/src/Model/Model_Session.cpp index 10bd28f20..065e6276d 100644 --- a/src/Model/Model_Session.cpp +++ b/src/Model/Model_Session.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -165,6 +166,29 @@ std::list Model_Session::redoList() return ROOT_DOC->redoList(); } +ModelAPI_Plugin* Model_Session::getPlugin(const std::string& thePluginName) +{ + if (myPluginObjs.find(thePluginName) == myPluginObjs.end()) { + // before load the used plugins + if (myUsePlugins.find(thePluginName) != myUsePlugins.end()) { + std::string aUse = myUsePlugins[thePluginName]; + std::stringstream aUseStream(aUse); + std::string aPluginName; + while (std::getline(aUseStream, aPluginName, ',')) { + if (myPluginObjs.find(aPluginName) == myPluginObjs.end()) + getPlugin(aPluginName); + } + } + // load plugin library if not yet done + Config_ModuleReader::loadPlugin(thePluginName); + } + if (myPluginObjs.find(thePluginName) == myPluginObjs.end()) { + Events_InfoMessage("Model_Session", "Can not load plugin '%1'").arg(thePluginName).send(); + return NULL; + } + return myPluginObjs[thePluginName]; +} + FeaturePtr Model_Session::createFeature(std::string theFeatureID, Model_Document* theDocOwner) { if (this != myImpl) { @@ -178,26 +202,20 @@ FeaturePtr Model_Session::createFeature(std::string theFeatureID, Model_Document std::pair& aPlugin = myPlugins[theFeatureID]; // plugin and doc kind if (!aPlugin.second.empty() && aPlugin.second != theDocOwner->kind()) { Events_InfoMessage("Model_Session", - "Feature '%1' can be created only in document '%2' by the XML definition") - .arg(theFeatureID).arg(aPlugin.second).send(); + "Feature '%1' can be created only in document '%2' by the XML definition") + .arg(theFeatureID).arg(aPlugin.second).send(); return FeaturePtr(); } - myCurrentPluginName = aPlugin.first; - if (myPluginObjs.find(myCurrentPluginName) == myPluginObjs.end()) { - // load plugin library if not yet done - Config_ModuleReader::loadPlugin(myCurrentPluginName); - } - if (myPluginObjs.find(myCurrentPluginName) != myPluginObjs.end()) { - FeaturePtr aCreated = myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID); + ModelAPI_Plugin* aPluginObj = getPlugin(aPlugin.first); + if (aPluginObj) { + FeaturePtr aCreated = aPluginObj->createFeature(theFeatureID); if (!aCreated) { - Events_InfoMessage("Model_Session", - "Can not initialize feature '%1' in plugin '%2'") - .arg(theFeatureID).arg(myCurrentPluginName).send(); + Events_InfoMessage("Model_Session", "Can not initialize feature '%1' in plugin '%2'") + .arg(theFeatureID).arg(aPlugin.first).send(); } return aCreated; } else { - Events_InfoMessage("Model_Session", - "Can not load plugin '%1'").arg(myCurrentPluginName).send(); + Events_InfoMessage("Model_Session", "Can not load plugin '%1'").arg(aPlugin.first).send(); } } else { Events_InfoMessage("Model_Session", @@ -416,6 +434,7 @@ Model_Session::Model_Session() aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED), 0, true); aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED), 0, true); aLoop->registerListener(this, Events_Loop::eventByName(EVENT_VALIDATOR_LOADED)); + aLoop->registerListener(this, Events_Loop::eventByName(Config_PluginMessage::EVENT_ID())); } void Model_Session::processEvent(const std::shared_ptr& theMessage) @@ -423,6 +442,7 @@ void Model_Session::processEvent(const std::shared_ptr& theMessa static const Events_ID kFeatureEvent = Events_Loop::eventByName(Config_FeatureMessage::MODEL_EVENT()); static const Events_ID kValidatorEvent = Events_Loop::eventByName(EVENT_VALIDATOR_LOADED); + static const Events_ID kPluginEvent = Events_Loop::eventByName(Config_PluginMessage::EVENT_ID()); if (theMessage->eventID() == kFeatureEvent) { const std::shared_ptr aMsg = std::dynamic_pointer_cast(theMessage); @@ -463,6 +483,15 @@ void Model_Session::processEvent(const std::shared_ptr& theMessa aMsg->parameters()); } } + } else if (theMessage->eventID() == kPluginEvent) { // plugin is started to load + std::shared_ptr aMsg = + std::dynamic_pointer_cast(theMessage); + if (aMsg.get()) { + myCurrentPluginName = aMsg->pluginId(); + if (!aMsg->uses().empty()) { + myUsePlugins[myCurrentPluginName] = aMsg->uses(); + } + } } else { // create/update/delete if (myCheckTransactions && !isOperation()) Events_InfoMessage("Model_Session", diff --git a/src/Model/Model_Session.h b/src/Model/Model_Session.h index 89b9d548c..7342ae2d3 100644 --- a/src/Model/Model_Session.h +++ b/src/Model/Model_Session.h @@ -44,6 +44,8 @@ class Model_Session : public ModelAPI_Session, public Events_Listener std::map myPluginObjs; ///< instances of the already plugins std::string myCurrentPluginName; ///< name of the plugin that must be loaded currently std::shared_ptr myCurrentDoc; ///< current working document + ///< map from plugin id to plugins which are used by it (must be loaded before this one) + std::map myUsePlugins; /// if true, generates error if document is updated outside of transaction bool myCheckTransactions; @@ -149,6 +151,9 @@ class Model_Session : public ModelAPI_Session, public Events_Listener /// Creates the feature object using plugins functionality FeaturePtr createFeature(std::string theFeatureID, Model_Document* theDocOwner); + /// Get the plugin by name. If it is not loaded, load plugin. + ModelAPI_Plugin* getPlugin(const std::string& thePluginName); + friend class Model_Document; friend class Model_Objects; }; diff --git a/src/ModuleBase/ModuleBase_IconFactory.cpp b/src/ModuleBase/ModuleBase_IconFactory.cpp index 067ef6ea5..a691efd32 100644 --- a/src/ModuleBase/ModuleBase_IconFactory.cpp +++ b/src/ModuleBase/ModuleBase_IconFactory.cpp @@ -62,10 +62,9 @@ QPixmap ModuleBase_IconFactory::loadPixmap(const QString& theValue) QPixmap aPixmap(theValue); if (aPixmap.isNull()) { - std::string aPluginPath = Config_XMLReader::pluginConfigFile(); - QString aPath = QString::fromStdString(aPluginPath) + QDir::separator() + theValue; - if (QFile::exists(aPath)) - aPixmap = QPixmap(aPath); + std::string aPath = Config_XMLReader::findConfigFile(theValue.toStdString()); + if (!aPath.empty()) + aPixmap = QPixmap(QString::fromStdString(aPath)); } return aPixmap; } @@ -75,10 +74,9 @@ QImage ModuleBase_IconFactory::loadImage(const QString& theValue) QImage anImage(theValue); if (anImage.isNull()) { - std::string aPluginPath = Config_XMLReader::pluginConfigFile(); - QString aPath = QString::fromStdString(aPluginPath) + QDir::separator() + theValue; - if (QFile::exists(aPath)) - anImage = QImage(aPath); + std::string aPath = Config_XMLReader::findConfigFile(theValue.toStdString()); + if (!aPath.empty()) + anImage = QImage(QString::fromStdString(aPath)); } return anImage; } -- 2.39.2