1 #include "pluginmanager.h"
2 #include <QObject>
3 #include <QtScript/QtScript>
4 #include <vcg/complex/algorithms/create/platonic.h>
5
6 #include "scriptinterface.h"
7 #include "mlexception.h"
8
9
DLLExtension()10 static QString DLLExtension() {
11 #if defined(Q_OS_WIN)
12 return QString("dll");
13 #elif defined(Q_OS_MAC)
14 return QString("dylib");
15 #else
16 return QString("so");
17 #endif
18 assert(0 && "Unknown Operative System. Please Define the appropriate dynamic library extension");
19 return QString();
20 }
21
DLLFileNamePreamble()22 static QString DLLFileNamePreamble() {
23 #if defined(Q_OS_WIN)
24 return QString("");
25 #elif defined(Q_OS_MAC)
26 return QString("lib");
27 #else
28 return QString("lib");
29 #endif
30 assert(0 && "Unknown Operative System. Please Define the appropriate dynamic library preamble");
31 return QString();
32 }
33
PluginManager()34 PluginManager::PluginManager()
35 :/*currentDocInterface(NULL),*/scriptplugcode()
36 {
37 //pluginsDir=QDir(getPluginDirPath());
38 // without adding the correct library path in the mac the loading of jpg (done via qt plugins) fails
39 //qApp->addLibraryPath(getPluginDirPath());
40 //qApp->addLibraryPath(getBaseDirPath());
41 }
42
~PluginManager()43 PluginManager::~PluginManager()
44 {
45 meshIOPlug.clear();
46 meshFilterPlug.clear();
47 meshRenderPlug.clear();
48 meshDecoratePlug.clear();
49 for (MeshCommonInterface* plugin : ownerPlug)
50 delete plugin;
51 ownerPlug.clear();
52
53 for (int ii = 0; ii < meshEditInterfacePlug.size(); ++ii)
54 delete meshEditInterfacePlug[ii];
55 meshEditInterfacePlug.clear();
56
57 for (int ii = 0; ii < meshlabXMLFilterPlug.size(); ++ii)
58 delete meshlabXMLFilterPlug[ii];
59 meshlabXMLFilterPlug.clear();
60
61 for (int ii = 0; ii < xmlpluginfo.size(); ++ii)
62 MLXMLPluginInfo::destroyXMLPluginInfo(xmlpluginfo[ii]);
63 xmlpluginfo.clear();
64 }
65
66
67
loadPlugins(RichParameterSet & defaultGlobal)68 void PluginManager::loadPlugins(RichParameterSet& defaultGlobal)
69 {
70 pluginsDir = QDir(getDefaultPluginDirPath());
71 // without adding the correct library path in the mac the loading of jpg (done via qt plugins) fails
72 qApp->addLibraryPath(getDefaultPluginDirPath());
73 qApp->addLibraryPath(getBaseDirPath());
74 QStringList pluginfilters;
75
76 pluginfilters << QString("*." + DLLExtension());
77 pluginfilters << "*.xml";
78
79 //only the file with extension pluginfilters will be listed by function entryList()
80 pluginsDir.setNameFilters(pluginfilters);
81
82 qDebug("Current Plugins Dir is: %s ", qUtf8Printable(pluginsDir.absolutePath()));
83 scriptplugcode = "";
84 ScriptAdapterGenerator gen;
85 scriptplugcode += gen.mergeOptParamsCodeGenerator() + "\n";
86 scriptplugcode += MLXMLUtilityFunctions::pluginsNameSpace() + " = { };\n";
87 foreach(QString fileName, pluginsDir.entryList(QDir::Files))
88 {
89 // qDebug() << fileName;
90 QString absfilepath = pluginsDir.absoluteFilePath(fileName);
91 QFileInfo fin(absfilepath);
92 if (fin.suffix() == "xml")
93 {
94 try
95 {
96 MLXMLPluginInfo* pluginfo = loadXMLPlugin(fileName);
97 if (pluginfo == NULL)
98 {
99 QString err;
100 err = "WARNING! " + fileName + " xml-based plugin was not found!";
101 throw MLException(err);
102 }
103 // //WARNING!!!! Why this piece of code is not inside the loadXMLPlugin function?!?!?!?!?!?!
104 // //Because one day, hopefully, the RichParameterSet class (the old parameters system) will disappear and we don't want to spread it inside the XML functions
105
106 // if (pluginfo != NULL)
107 // {
108 // QString pluginprogname = pluginfo->pluginScriptName();
109 // foreach(QString filtername, pluginfo->filterNames())
110 // {
111 // QString filterprogname = pluginfo->filterAttribute(filtername, MLXMLElNames::filterScriptFunctName);
112 // foreach(MLXMLPluginInfo::XMLMap params, pluginfo->filterParametersExtendedInfo(filtername))
113 // {
114 // if (params[MLXMLElNames::paramIsPersistent] == QString("true"))
115 // {
116 // //RichParameter* rp = NULL;
117 // QString completepreamble = MLXMLUtilityFunctions::completeFilterProgrammingName(MLXMLUtilityFunctions::pluginsNameSpace(), pluginprogname, filterprogname);
118 // /*RichParameterAdapter::create(completepreamble,params, &rp);
119 // if (rp != NULL)
120 // defaultGlobal.addParam(rp);*/
121 // }
122 // }
123 // }
124 // }
125 }
126 catch (MeshLabXMLParsingException& e)
127 {
128 qDebug() << e.what();
129 }
130 }
131 else
132 {
133 QPluginLoader loader(absfilepath);
134 QObject *plugin = loader.instance();
135 if (plugin)
136 {
137 pluginsLoaded.push_back(fileName);
138 MeshCommonInterface *iCommon = nullptr;
139 MeshFilterInterface *iFilter = qobject_cast<MeshFilterInterface *>(plugin);
140 if (iFilter)
141 {
142 iCommon = iFilter;
143 meshFilterPlug.push_back(iFilter);
144 foreach(QAction *filterAction, iFilter->actions())
145 {
146 filterAction->setData(QVariant(fileName));
147 actionFilterMap.insert(filterAction->text(), filterAction);
148 stringFilterMap.insert(filterAction->text(), iFilter);
149 iFilter->initGlobalParameterSet(filterAction, defaultGlobal);
150 if(iFilter->getClass(filterAction)==MeshFilterInterface::Generic)
151 throw MLException("Missing class for " +fileName+filterAction->text());
152 if(iFilter->getRequirements(filterAction) == int(MeshModel::MM_UNKNOWN))
153 throw MLException("Missing requirements for " +fileName+filterAction->text());
154 if(iFilter->getPreConditions(filterAction) == int(MeshModel::MM_UNKNOWN))
155 throw MLException("Missing preconditions for "+fileName+filterAction->text());
156 if(iFilter->postCondition(filterAction) == int(MeshModel::MM_UNKNOWN ))
157 throw MLException("Missing postcondition for "+fileName+filterAction->text());
158 if(iFilter->filterArity(filterAction) == MeshFilterInterface::UNKNOWN_ARITY )
159 throw MLException("Missing Arity for " +fileName+filterAction->text());
160 }
161 }
162 MeshIOInterface *iIO = qobject_cast<MeshIOInterface *>(plugin);
163 if (iIO)
164 {
165 iCommon = iIO;
166 meshIOPlug.push_back(iIO);
167 iIO->initGlobalParameterSet(NULL, defaultGlobal);
168 }
169
170 MeshDecorateInterface *iDecorator = qobject_cast<MeshDecorateInterface *>(plugin);
171 if (iDecorator)
172 {
173 iCommon = iDecorator;
174 meshDecoratePlug.push_back(iDecorator);
175 foreach(QAction *decoratorAction, iDecorator->actions())
176 {
177 decoratorActionList.push_back(decoratorAction);
178 iDecorator->initGlobalParameterSet(decoratorAction, defaultGlobal);
179 }
180 }
181
182 MeshRenderInterface *iRender = qobject_cast<MeshRenderInterface *>(plugin);
183 if (iRender)
184 {
185 iCommon = iRender;
186 meshRenderPlug.push_back(iRender);
187 }
188
189 MeshEditInterfaceFactory *iEditFactory = qobject_cast<MeshEditInterfaceFactory *>(plugin);
190 if (iEditFactory)
191 {
192 meshEditInterfacePlug.push_back(iEditFactory);
193 foreach(QAction* editAction, iEditFactory->actions())
194 editActionList.push_back(editAction);
195 }
196 else if (iCommon)
197 {
198 ownerPlug.push_back(iCommon);
199 } else {
200 // qDebug("Plugin %s was loaded, but could not be casted to any known type.", qUtf8Printable(fileName));
201 }
202 }
203 else
204 qDebug() << loader.errorString();
205 }
206 }
207 knownIOFormats();
208 }
209
210 // Search among all the decorator plugins the one that contains a decoration with the given name
getDecoratorInterfaceByName(const QString & name)211 MeshDecorateInterface *PluginManager::getDecoratorInterfaceByName(const QString& name)
212 {
213 foreach(MeshDecorateInterface *tt, this->meshDecoratePlugins())
214 {
215 foreach( QAction *ac, tt->actions())
216 if( name == tt->decorationName(ac) ) return tt;
217 }
218 assert(0);
219 return 0;
220 }
221
222 /*
223 This function create a map from filtername to dummy RichParameterSet.
224 containing for each filtername the set of parameter that it uses.
225 */
generateFilterParameterMap()226 QMap<QString, RichParameterSet> PluginManager::generateFilterParameterMap()
227 {
228 QMap<QString, RichParameterSet> FPM;
229 MeshDocument md;
230 MeshModel* mm = md.addNewMesh("", "dummy", true);
231 vcg::tri::Tetrahedron<CMeshO>(mm->cm);
232 mm->updateDataMask(MeshModel::MM_ALL);
233 QMap<QString, QAction*>::iterator ai;
234 for (ai = this->actionFilterMap.begin(); ai != this->actionFilterMap.end(); ++ai)
235 {
236 QString filterName = ai.key();// ->filterName();
237 //QAction act(filterName,NULL);
238 RichParameterSet rp;
239 stringFilterMap[filterName]->initParameterSet(ai.value(), md, rp);
240 FPM[filterName] = rp;
241 }
242 return FPM;
243 }
244
osDependentFileBaseName(const QString & plname)245 QString PluginManager::osDependentFileBaseName(const QString &plname)
246 {
247 return (DLLFileNamePreamble() + plname + "." + DLLExtension());
248 }
249
getBaseDirPath()250 QString PluginManager::getBaseDirPath()
251 {
252 QDir baseDir(qApp->applicationDirPath());
253
254 #if defined(Q_OS_WIN)
255 // Windows:
256 // during development with visual studio binary could be in the debug/release subdir.
257 // once deployed plugins dir is in the application directory, so
258 if (baseDir.dirName() == "debug" || baseDir.dirName() == "release") baseDir.cdUp();
259 #endif
260
261 #if defined(Q_OS_MAC)
262 // Mac: during developmentwith xcode and well deployed the binary is well buried.
263 for(int i=0;i<6;++i){
264 if(baseDir.exists("plugins")) break;
265 baseDir.cdUp();
266 }
267 qDebug("The base dir is %s", qUtf8Printable(baseDir.absolutePath()));
268 #endif
269 return baseDir.absolutePath();
270 }
271
getDefaultPluginDirPath()272 QString PluginManager::getDefaultPluginDirPath()
273 {
274 QDir pluginsDir(getBaseDirPath());
275 #if defined(Q_OS_WIN)
276 QString d = pluginsDir.dirName();
277 QString dLower = d.toLower();
278 if (dLower == "release" || dLower == "relwithdebinfo" || dLower == "debug" ||
279 dLower == "minsizerel") {
280 // This is a configuration directory for MS Visual Studio.
281 pluginsDir.cdUp();
282 } else {
283 d.clear();
284 }
285 #endif
286 if (pluginsDir.exists("plugins")) {
287 pluginsDir.cd("plugins");
288
289 #if defined(Q_OS_WIN)
290 // Re-apply the configuration dir, if any.
291 if (!d.isEmpty() && pluginsDir.exists(d)) {
292 pluginsDir.cd(d);
293 }
294 #endif
295
296 return pluginsDir.absolutePath();
297 }
298 #if !defined(Q_OS_MAC) && !defined(Q_OS_WIN)
299 if (pluginsDir.dirName() == "bin") {
300 pluginsDir.cdUp();
301 pluginsDir.cd("lib");
302 pluginsDir.cd("meshlab");
303 if (pluginsDir.exists("plugins")) {
304 pluginsDir.cd("plugins");
305 return pluginsDir.absolutePath();
306 }
307 }
308 #endif
309 //QMessageBox::warning(0,"Meshlab Initialization","Serious error. Unable to find the plugins directory.");
310 qDebug("Meshlab Initialization: Serious error. Unable to find the plugins directory.");
311 return {};
312 }
313
314
315
knownIOFormats()316 void PluginManager::knownIOFormats()
317 {
318 for (int inpOut = 0; inpOut < 2; ++inpOut)
319 {
320 QStringList* formatFilters = NULL;
321 QString allKnownFormatsFilter = QObject::tr("All known formats (");
322 for (QVector<MeshIOInterface*>::iterator itIOPlugin = meshIOPlug.begin(); itIOPlugin != meshIOPlug.end(); ++itIOPlugin)
323 {
324 MeshIOInterface* pMeshIOPlugin = *itIOPlugin;
325 QList<MeshIOInterface::Format> format;
326 QMap<QString, MeshIOInterface*>* map = NULL;
327 if (inpOut == int(IMPORT))
328 {
329 map = &allKnowInputFormats;
330 formatFilters = &inpFilters;
331 format = pMeshIOPlugin->importFormats();
332 }
333 else
334 {
335 map = &allKnowOutputFormats;
336 formatFilters = &outFilters;
337 format = pMeshIOPlugin->exportFormats();
338 }
339 for (QList<MeshIOInterface::Format>::iterator itf = format.begin(); itf != format.end(); ++itf)
340 {
341 MeshIOInterface::Format currentFormat = *itf;
342
343 QString currentFilterEntry = currentFormat.description + " (";
344
345 //a particular file format could be associated with more than one file extension
346 QStringListIterator itExtension(currentFormat.extensions);
347 while (itExtension.hasNext())
348 {
349 QString currentExtension = itExtension.next().toLower();
350 if (!map->contains(currentExtension))
351 {
352 map->insert(currentExtension, pMeshIOPlugin);
353 allKnownFormatsFilter.append(QObject::tr(" *."));
354 allKnownFormatsFilter.append(currentExtension);
355 }
356 currentFilterEntry.append(QObject::tr(" *."));
357 currentFilterEntry.append(currentExtension);
358 }
359 currentFilterEntry.append(')');
360 formatFilters->append(currentFilterEntry);
361 }
362
363 }
364 allKnownFormatsFilter.append(')');
365 if (formatFilters != NULL)
366 formatFilters->push_front(allKnownFormatsFilter);
367 }
368 }
369
370 //void PluginManager::updateDocumentScriptBindings(MeshDocument& doc )
371 //{
372 // //WARNING!
373 // //all the currentDocInterface created will be destroyed by QT when the MeshDocument destructor has been called
374 // currentDocInterface = new MeshDocumentSI(&doc);
375 // QScriptValue val = env.newQObject(currentDocInterface);
376 // env.globalObject().setProperty(ScriptAdapterGenerator::meshDocVarName(),val);
377 //}
378
pluginsCode() const379 QString PluginManager::pluginsCode() const
380 {
381 return scriptplugcode;
382 }
383
loadXMLPlugin(const QString & fileName)384 MLXMLPluginInfo* PluginManager::loadXMLPlugin(const QString& fileName)
385 {
386 ScriptAdapterGenerator gen;
387 QString absfilepath = pluginsDir.absoluteFilePath(fileName);
388 QFileInfo fin(absfilepath);
389 if (fin.suffix() == "xml")
390 {
391
392 QString dllfile = osDependentFileBaseName(fin.completeBaseName());
393
394 MeshLabXMLFilterContainer fc;
395 //fc.filterInterface = NULL;
396 XMLMessageHandler xmlErr;
397 MLXMLPluginInfo* pluginfo = MLXMLPluginInfo::createXMLPluginInfo(absfilepath, MLXMLUtilityFunctions::xmlSchemaFile(), xmlErr);
398 if (pluginfo != NULL)
399 {
400 xmlpluginfo << pluginfo;
401 fc.xmlInfo = xmlpluginfo[xmlpluginfo.size() - 1];
402 // QStringList fn = fc.xmlInfo->filterNames();
403 QObject* par = NULL;
404 if (pluginsDir.exists(dllfile))
405 {
406 QPluginLoader loader(fin.absoluteDir().absolutePath() + "/" + dllfile);
407 QObject* plugin = loader.instance();
408 MeshLabFilterInterface* iXMLfilter = qobject_cast<MeshLabFilterInterface *>(plugin);
409 if (iXMLfilter != NULL)
410 {
411 meshlabXMLFilterPlug << iXMLfilter;
412 fc.filterInterface = meshlabXMLFilterPlug[meshlabXMLFilterPlug.size() - 1];
413 par = plugin;
414 }
415 }
416 else
417 {
418 // we have loaded an xml without the corresponding dll. Let's check that it is a pure javascript plugin
419 bool foundANonJavaScriptFilter = false;
420 foreach(QString filterName, pluginfo->filterNames())
421 {
422 if (pluginfo->filterElement(filterName, MLXMLElNames::filterJSCodeTag).isEmpty())
423 foundANonJavaScriptFilter = true;
424 }
425 if (foundANonJavaScriptFilter)
426 {
427 throw(MeshLabXMLParsingException("We are trying to load a xml file that does not correspond to any dll or javascript code; please delete all the spurious xml files"));
428 }
429 par = new QObject();
430 }
431 QString pname = pluginfo->pluginScriptName();
432 if (pname != "")
433 {
434 QString plugnamespace = MLXMLUtilityFunctions::pluginsNameSpace() + "." + pname;
435 //pluginnamespaces << plugnamespace;
436 scriptplugcode += plugnamespace + " = { };\n";
437 QStringList filters = pluginfo->filterNames();
438 foreach(QString filter, filters)
439 {
440 MLXMLPluginInfo::XMLMapList paramsinfo = pluginfo->filterParametersExtendedInfo(filter);
441 QString completename = plugnamespace;
442 fc.act = new QAction(filter, par);
443 fc.act->setData(QVariant(dllfile));
444 stringXMLFilterMap.insert(filter, fc);
445 QString filterFunction = pluginfo->filterScriptCode(filter);
446 if (filterFunction == "")
447 filterFunction = gen.funCodeGenerator(filter, *pluginfo);
448 else
449 filterFunction = "{" + filterFunction + "};";
450 QString jname = pluginfo->filterAttribute(filter, MLXMLElNames::filterScriptFunctName);
451 completename += "." + jname;
452 //filterscriptnames << completename;
453 scriptplugcode += completename + " = " + filterFunction + "\n";
454 completename += ".";
455 //for (MLXMLPluginInfo::XMLMap& paraminfo : paramsinfo)
456 //{
457 // if (paraminfo[MLXMLElNames::paramIsPersistent] == "true")
458 // {
459 // QString fullvarname = completename + paraminfo[MLXMLElNames::paramName];
460 // //scriptplugcode += namespaceVariableAssignment(fullvarname, paraminfo[MLXMLElNames::paramDefExpr]);
461 // paraminfo[MLXMLElNames::paramDefExpr] = fullvarname;
462 // }
463 //}
464
465 completename += "(" + gen.parNames(filter, *pluginfo) + ")";
466 LibraryElementInfo li;
467 li.completename = completename;
468 li.help = pluginfo->filterHelp(filter);
469 libinfolist << li;
470 }
471 }
472 return pluginfo;
473 }
474 else
475 {
476 QString err = xmlErr.statusMessage();
477 qDebug("Error in XMLFile: %s - line: %d, column: %d - %s", qUtf8Printable(fileName), xmlErr.line(), xmlErr.column(), qUtf8Printable(err));
478 }
479 }
480 return nullptr;
481 }
482
483 //MLXMLPluginInfo* PluginManager::getXMLPluginInfo( const QString& plugname )
484 //{
485 // for(int ii = 0;ii < xmlpluginfo.size();++ii)
486 // if (xmlpluginfo[ii]->pluginFilePath() != plugname)
487 // return xmlpluginfo[ii];
488 // return NULL;
489 //}
490
deleteXMLPlugin(const QString & plugscriptname)491 void PluginManager::deleteXMLPlugin(const QString& plugscriptname)
492 {
493 bool found = false;
494 int ii = 0;
495 while ((ii < xmlpluginfo.size()) && !found)
496 {
497 if (xmlpluginfo[ii]->pluginScriptName() == plugscriptname)
498 found = true;
499 else
500 ++ii;
501 }
502 if (found)
503 {
504 QStringList removefilters;
505 QSet<MeshLabFilterInterface*> tobedeleted;
506 for (QMap<QString, MeshLabXMLFilterContainer>::iterator it = stringXMLFilterMap.begin(); it != stringXMLFilterMap.end();)
507 {
508 if (xmlpluginfo[ii] == it.value().xmlInfo)
509 {
510 QString rem = it.key();
511 if (it.value().filterInterface != NULL)
512 tobedeleted.insert(it.value().filterInterface);
513 ++it;
514 stringXMLFilterMap.remove(rem);
515 }
516 else
517 ++it;
518 }
519 MLXMLPluginInfo* tmp = xmlpluginfo[ii];
520 xmlpluginfo.remove(ii);
521 MLXMLPluginInfo::destroyXMLPluginInfo(tmp);
522 for (QSet<MeshLabFilterInterface*>::iterator it = tobedeleted.begin(); it != tobedeleted.end(); ++it)
523 {
524 int ii = meshlabXMLfilterPlugins().indexOf(*it);
525 MeshLabFilterInterface* fi = meshlabXMLfilterPlugins()[ii];
526 meshlabXMLfilterPlugins().remove(ii);
527 delete fi;
528 }
529 }
530 }
531
osIndependentPluginName(const QString & plname)532 QString PluginManager::osIndependentPluginName(const QString& plname)
533 {
534 QFileInfo fi(plname);
535 QString res = fi.baseName();
536 QString pref = DLLFileNamePreamble();
537 return res.remove(0, pref.size());
538 }
539
540