total99resources.cpp
1 /********************************************************************************
2  * FARSA - Total99 *
3  * Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "total99resources.h"
21 #include <cstdlib>
22 #include <QApplication>
23 #include <QFile>
24 #include <QDir>
25 #include <QFileInfo>
26 #include <QPluginLoader>
27 #include <QStringList>
28 #include <QRegExp>
29 #include <QFileInfo>
30 #include <QSet>
31 #include <QStringList>
32 #include "configurationparameters.h"
33 #include "factory.h"
34 #include "farsaplugin.h"
35 #include "evorobotviewer.h"
36 #include "logger.h"
37 #include "dependencysorter.h"
38 
39 // We need this to set the DLL search path on Windows
40 #ifdef FARSA_WIN
41  #include "Windows.h"
42 #endif
43 
44 // All the suff below is to avoid warnings on Windows about the use of the
45 // unsafe function getenv
46 #if defined(_MSC_VER)
47  #pragma warning(push)
48  #pragma warning(disable:4996)
49 #endif
50 
51 namespace farsa {
52 
53 QString Total99Resources::findResource( QString resourceName ) {
54  //--- search in the user directory
55  if ( QFile::exists( confUserPath + "/" + resourceName ) ) {
56  return ( confUserPath + "/" + resourceName );
57  }
58  //--- search in the template user directory
59  if ( QFile::exists( confUserPath + "/templates/" + uiTemplate + "/" + resourceName ) ) {
60  return ( confUserPath + "/templates/" + uiTemplate + "/" + resourceName );
61  }
62  //--- search in the global directory
63  if ( QFile::exists( confBasePath + "/" + resourceName ) ) {
64  return ( confBasePath + "/" + resourceName );
65  }
66  //--- search in the global template directory
67  if ( QFile::exists( confBasePath + "/templates/" + uiTemplate + "/" + resourceName ) ) {
68  return ( confBasePath + "/templates/" + uiTemplate + "/" + resourceName );
69  }
70  //--- not find anything
71  return QString();
72 }
73 
78 // QString Total99Resources::pluginUserPath;
81 
82 bool Total99Resources::loadPlugin(QString filename)
83 {
84 #ifdef FARSA_WIN
85  // On Windows, we first have to change the DLL search path to include the directory with plugins
86  SetDllDirectory(pluginBasePath.toLatin1().data());
87 #endif
88 
89  // Loading the plugin
90  QString errorString;
91  FarsaPlugin* plugin = loadSinglePlugin(filename, &errorString);
92 
93  if (plugin == NULL) {
94  // Error loading the plugin
95  Logger::error("ERROR LOADING PLUGIN: " + errorString);
96  return false;
97  }
98  Logger::info("Loaded plugin \"" + filename + "\"");
99 
100  // The object to sort plugin by dependency
101  DependencySorter<QString> pluginDependencies;
102 
103  // Getting plugin dependencies. We store loaded plugin in a map whose key is the plugin name (we also use this
104  // to avoid loading the same plugin twice) and we keep a list of dependencies yet to load. Here it is not
105  // important the loading order (while the registration order is, see below).
106  QStringList dependenciesToLoad = plugin->getDependencies();
107  QMap<QString, FarsaPlugin*> loadedDependencies;
108  while (!dependenciesToLoad.empty()) {
109  // Taking the first dependency
110  QString curDep = dependenciesToLoad.takeFirst();
111 
112  // If the plugin has already been loaded, skipping it
113  if (loadedDependencies.contains(curDep)) {
114  continue;
115  }
116 
117  // Loading the plugin
118  loadedDependencies[curDep] = loadSinglePlugin(curDep, &errorString);
119  if (loadedDependencies[curDep] == NULL) {
120  // Error loading the plugin
121  Logger::error("ERROR LOADING PLUGIN: " + errorString);
122  return false;
123  }
124  Logger::info("Loaded plugin \"" + curDep + "\"");
125 
126  // Adding the dependencies of the plugin we have just loaded
127  QStringList newDependencies = loadedDependencies[curDep]->getDependencies();
128  pluginDependencies.add(curDep, newDependencies);
129  foreach (QString d, newDependencies) {
130  dependenciesToLoad.append(d);
131  }
132  }
133 
134  // Now that we have loaded all plugins, we need to sort the dependencies, so to call registration in the
135  // correct order
136  try {
137  QStringList sortedDependencies = pluginDependencies.sort();
138  foreach (QString d, sortedDependencies) {
139  loadedDependencies[d]->registerTypesOnFactory();
140  }
141  } catch (CircularDependencyException &) {
142  Logger::error("ERROR LOADING PLUGIN: circular dependency found");
143  return false;
144  }
145 
146  // We can finally register the plugin we loaded first
147  plugin->registerTypesOnFactory();
148 
149 #ifdef FARSA_WIN
150  // Reverting to the default behaviour
151  SetDllDirectory(NULL);
152 #endif
153 
154  return true;
155 }
156 
158 {
159  foreach (QString pluginfile, dir.entryList()) {
160  if (!QLibrary::isLibrary(pluginfile)) {
161  continue;
162  }
163 
164  // Building a regular expression to actually load only files containing farsaPlugin
165  QRegExp r(".*farsaPlugin.*");
166  if (r.indexIn(pluginfile) == -1) {
167  continue;
168  }
169  if (loadPlugin(dir.absoluteFilePath(pluginfile))) {
170  Logger::info( "Loaded Plugin \"" + pluginfile + "\"");
171  }
172  }
173 }
174 
176 {
177  // First loading plugins in the pluginFile:X parameters
178  QStringList plugins = params.getParametersWithPrefixList("TOTAL99", "pluginFile" );
179  foreach (QString param, plugins) {
180  QString value = params.getValue("TOTAL99/" + param);
181  if (!QFileInfo(value).isFile()) {
182  // Trying to see if the plugin is in the global plugin directory
183  value = pluginBasePath + "/" + value + ".farsaPlugin" + pluginSuffix;
184  if (!QFileInfo(value).isFile()) {
185  Logger::warning( "Ignoring un-existing plugin \"" + params.getValue("TOTAL99/" + param) + "\"" );
186  continue;
187  }
188  }
189  loadPlugin(value);
190  }
191 
192  // Then loading all pugins from the pluginPath:X parameters
193  QStringList pluginPaths = params.getParametersWithPrefixList( "TOTAL99", "pluginPath" );
194  foreach( QString param, pluginPaths ) {
195  QString value = params.getValue( "TOTAL99/"+param );
196  if ( ! QFileInfo( value ).isDir() ) {
197  Logger::warning( "Ignoring un-existing plugin path \"" + value + "\"" );
198  continue;
199  }
200  loadPlugins( QDir( value ) );
201  }
202 }
203 
205 #ifdef FARSA_WIN
206  confBasePath = qApp->applicationDirPath() + "/../conf";
207  pluginConfigBasePath = qApp->applicationDirPath() + "/../plugins";
208 #else
209  confBasePath = qApp->applicationDirPath() + "/../share/FARSA/conf";
210  pluginConfigBasePath = qApp->applicationDirPath() + "/../share/FARSA/plugins";
211 #endif
212  pluginBasePath = qApp->applicationDirPath() + "/../lib/FARSA/plugins";
213 #ifdef FARSA_LINUX
214  confUserPath = QString(getenv("HOME")) + "/.FARSA/total99";
215 // pluginUserPath = QString(getenv("HOME")) + "/.FARSA/total99/plugins";
216  pluginSuffix = ".so";
217 #endif
218 #ifdef FARSA_MAC
219  confUserPath = QString(getenv("HOME")) + "/Library/Application Support/FARSA/Total99";
220 // pluginUserPath = QString(getenv("HOME")) + "/Library/Application Support/FARSA/Total99/plugins";
221  pluginSuffix = ".dylib";
222 #endif
223 #ifdef FARSA_WIN
224  confUserPath = QString(getenv("APPDATA")) + "/FARSA/Total99";
225 // pluginUserPath = QString(getenv("APPDATA")) + "/FARSA/Total99/plugins";
226 #ifdef FARSA_DEBUG
227  pluginSuffix = "d.dll";
228 #else
229  pluginSuffix = ".dll";
230 #endif
231 #endif
232 
233  QDir dir;
234  dir.mkpath( confUserPath );
235 // dir.mkpath( pluginUserPath );
236 
237  // NOTE: This has been removed because it is not documented and furthermore with the upcoming
238  // moving of big parts of code to plugins this would mean loading a lot of stuffs
239 // // Loading plugins from default directories
240 // loadPlugins( QDir( pluginBasePath ) );
241 // loadPlugins( QDir( pluginUserPath ) );
242 
243  uiTemplate = "kids";
244  Logger::info( "Total99 Resources Initialized" );
245 }
246 
247 FarsaPlugin* Total99Resources::loadSinglePlugin(QString filename, QString* errorString)
248 {
249  if (!QFileInfo(filename).isFile()) {
250  filename = pluginBasePath + "/" + filename + ".farsaPlugin" + pluginSuffix;
251  }
252 
253  // Trying to load the plugin
254  QPluginLoader loader(filename);
255  FarsaPlugin* plugin = qobject_cast<FarsaPlugin*>(loader.instance());
256  if ((plugin == NULL) && (errorString != NULL)) {
257  *errorString = "Error trying to load \"" + filename + "\", reason: " + loader.errorString();
258  }
259 
260  return plugin;
261 }
262 
263 }
264 
265 // All the suff below is to restore the warning state on Windows
266 #if defined(_MSC_VER)
267  #pragma warning(pop)
268 #endif
269 
static bool loadPlugin(QString filename)
Load a plugin.
static QString confBasePath
path to the base (global) configuration directory
static QString pluginBasePath
path to the base (global) plugin directory
static void initialize()
initialize data
static void loadPlugins(QDir dir)
Load all plugins found in the directory.
static QString pluginConfigBasePath
path to the base (global) directory with plugin configuration files
QStringList getParametersWithPrefixList(QString group, QString prefix) const
the interface for implement a plugin for adding new feature to FARSA
Definition: farsaplugin.h:47
void add(ElementType e, ElementType d)
virtual void registerTypesOnFactory()=0
register types on the Factory
static void info(QString msg)
static void error(QString msg)
virtual QStringList getDependencies()
returns the list of dependencies
Definition: farsaplugin.h:56
QString getValue(QString path, bool alsoMatchParents=false) const
static QString uiTemplate
path to the user plugin directory
static QString findResource(QString resourceName)
return the full path of the requested resource (in this case is a file name) It search in the followi...
static QString pluginSuffix
The suffix for plugins on the current operating system.
static void warning(QString msg)
static QString confUserPath
path to the user configuration directory
QList< ElementType > sort() const