File indexing completed on 2024-04-21 15:02:58

0001 /***************************************************************************
0002  * manager.cpp
0003  * This file is part of the KDE project
0004  * copyright (C)2004-2007 by Sebastian Sauer (mail@dipe.org)
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this program; see the file COPYING.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  ***************************************************************************/
0019 
0020 #include "manager.h"
0021 #include "interpreter.h"
0022 #include "action.h"
0023 #include "actioncollection.h"
0024 #include "kross_debug.h"
0025 
0026 #include <QObject>
0027 #include <QArgument>
0028 #include <QFile>
0029 #include <QRegExp>
0030 #include <QFileInfo>
0031 #include <QPointer>
0032 #include <QLibrary>
0033 #include <QCoreApplication>
0034 
0035 #include <klocalizedstring.h>
0036 
0037 extern "C"
0038 {
0039     typedef QObject *(*def_module_func)();
0040 }
0041 
0042 using namespace Kross;
0043 
0044 namespace Kross
0045 {
0046 
0047 /// @internal
0048 class Manager::Private
0049 {
0050 public:
0051     /// List of \a InterpreterInfo instances.
0052     QHash< QString, InterpreterInfo * > interpreterinfos;
0053 
0054     /// List of the interpreter names.
0055     QStringList interpreters;
0056 
0057     /// Loaded modules.
0058     QHash< QString, QPointer<QObject> > modules;
0059 
0060     /// The collection of \a Action instances.
0061     ActionCollection *collection;
0062 
0063     /// List with custom handlers for metatypes.
0064     QHash<QByteArray, MetaTypeHandler *> wrappers;
0065 
0066     /// Strict type handling enabled or disabled.
0067     bool strictTypesEnabled;
0068 };
0069 
0070 }
0071 
0072 Q_GLOBAL_STATIC(Manager, _self)
0073 
0074 Manager &Manager::self()
0075 {
0076     return *_self();
0077 }
0078 
0079 enum LibraryFunction
0080 {
0081     LibraryFunctionInterpreter,
0082     LibraryFunctionModule
0083 };
0084 
0085 static QFunctionPointer loadLibrary(const char *libname, enum LibraryFunction function)
0086 {
0087     QLibrary lib;
0088     QString libAbsoluteFilePath;
0089     foreach (const QString &path, QCoreApplication::instance()->libraryPaths()) {
0090         const QFileInfo &fileInfo = QFileInfo(path, libname);
0091         lib.setFileName(fileInfo.filePath());
0092         lib.setLoadHints(QLibrary::ExportExternalSymbolsHint);
0093         if (lib.load()) {
0094             libAbsoluteFilePath = fileInfo.absoluteFilePath();
0095             break;
0096         }
0097     }
0098 
0099     if (!lib.isLoaded()) {
0100 #ifdef KROSS_INTERPRETER_DEBUG
0101         if (function == LibraryFunctionInterpreter) {
0102             qCDebug(KROSS_LOG) << "Kross Interpreter '" << libname <<
0103                 "' not available: " << lib.errorString();
0104         } else if (function == LibraryFunctionModule) {
0105             qCDebug(KROSS_LOG) << "Kross Module '" << libname <<
0106                 "' not available: " << lib.errorString();
0107         } else {
0108             qCWarning(KROSS_LOG) << "Failed to load unknown type of '" <<
0109                 libname << "' library: " << lib.errorString();
0110         }
0111 #endif
0112         return nullptr;
0113     }
0114 
0115     const char* functionName = function == LibraryFunctionInterpreter ? "krossinterpreter" : "krossmodule";
0116     QFunctionPointer funcPtr = lib.resolve(functionName);
0117     if (!funcPtr) {
0118         qCWarning(KROSS_LOG) << QStringLiteral("Failed to resolve %1 in %2%3")
0119             .arg(functionName)
0120             .arg(lib.fileName())
0121             .arg(libAbsoluteFilePath.isEmpty() ? "" : QString(" (%1)").arg(libAbsoluteFilePath));
0122     }
0123     return funcPtr;
0124 }
0125 
0126 Manager::Manager()
0127     : QObject()
0128     , QScriptable()
0129     , ChildrenInterface()
0130     , d(new Private())
0131 {
0132     d->strictTypesEnabled = true;
0133     setObjectName("Kross");
0134     d->collection = new ActionCollection("main");
0135 
0136 #ifdef KROSS_PYTHON_LIBRARY
0137     if (QFunctionPointer funcPtr = loadLibrary(KROSS_PYTHON_LIBRARY, LibraryFunctionInterpreter)) {
0138         d->interpreterinfos.insert("python",
0139                                    new InterpreterInfo("python",
0140                                            funcPtr, // library
0141                                            "*.py", // file filter-wildcard
0142                                            QStringList() << "text/x-python" // mimetypes
0143                                                       )
0144                                   );
0145     }
0146 #endif
0147 
0148 #ifdef KROSS_RUBY_LIBRARY
0149     if (QFunctionPointer funcPtr = loadLibrary(KROSS_RUBY_LIBRARY, LibraryFunctionInterpreter)) {
0150         InterpreterInfo::Option::Map options;
0151         options.insert("safelevel", new InterpreterInfo::Option(
0152                            i18n("Level of safety of the Ruby interpreter"),
0153                            QVariant(0)));  // 0 -> unsafe, 4 -> very safe
0154         d->interpreterinfos.insert("ruby",
0155                                    new InterpreterInfo("ruby",
0156                                            funcPtr, // library
0157                                            "*.rb", // file filter-wildcard
0158                                            QStringList() << /* "text/x-ruby" << */ "application/x-ruby", // mimetypes
0159                                            options // options
0160                                                       )
0161                                   );
0162     }
0163 #endif
0164 
0165 #ifdef KROSS_JAVA_LIBRARY
0166     if (QFunctionPointer funcPtr = loadLibrary(KROSS_JAVA_LIBRARY, LibraryFunctionInterpreter)) {
0167         d->interpreterinfos.insert("java",
0168                                    new InterpreterInfo("java",
0169                                            funcPtr, // library
0170                                            "*.java *.class *.jar", // file filter-wildcard
0171                                            QStringList() << "application/java" // mimetypes
0172                                                       )
0173                                   );
0174     }
0175 #endif
0176 
0177 #ifdef KROSS_FALCON_LIBRARY
0178     if (QFunctionPointer funcPtr = loadLibrary(KROSS_FALCON_LIBRARY, LibraryFunctionInterpreter)) {
0179         d->interpreterinfos.insert("falcon",
0180                                    new InterpreterInfo("falcon",
0181                                            funcPtr, // library
0182                                            "*.fal", // file filter-wildcard
0183                                            QStringList() << "application/x-falcon" // mimetypes
0184                                                       )
0185                                   );
0186     }
0187 #endif
0188 
0189 #ifdef KROSS_QTSCRIPT_LIBRARY
0190     if (QFunctionPointer funcPtr = loadLibrary(KROSS_QTSCRIPT_LIBRARY, LibraryFunctionInterpreter)) {
0191         d->interpreterinfos.insert("qtscript",
0192                                    new InterpreterInfo("qtscript",
0193                                            funcPtr, // library
0194                                            "*.es", // file filter-wildcard
0195                                            QStringList() << "application/ecmascript" // mimetypes
0196                                                       )
0197                                   );
0198     }
0199 #endif
0200 
0201 #ifdef KROSS_LUA_LIBRARY
0202     if (QFunctionPointer funcPtr = loadLibrary(KROSS_LUA_LIBRARY, LibraryFunctionInterpreter)) {
0203         d->interpreterinfos.insert("lua",
0204                                    new InterpreterInfo("lua",
0205                                            funcPtr, // library
0206                                            "*.lua *.luac", // file filter-wildcard
0207                                            QStringList() << "application/x-lua" // mimetypes
0208                                                       )
0209                                   );
0210     }
0211 #endif
0212 
0213     // fill the list of supported interpreternames.
0214     QHash<QString, InterpreterInfo *>::Iterator it(d->interpreterinfos.begin());
0215     for (; it != d->interpreterinfos.end(); ++it)
0216         if (it.value()) {
0217             d->interpreters << it.key();
0218         }
0219     d->interpreters.sort();
0220 
0221     // publish ourself.
0222     ChildrenInterface::addObject(this, "Kross");
0223 }
0224 
0225 Manager::~Manager()
0226 {
0227     qDeleteAll(d->wrappers);
0228     qDeleteAll(d->interpreterinfos);
0229     qDeleteAll(d->modules);
0230     delete d->collection;
0231     delete d;
0232 }
0233 
0234 QHash< QString, InterpreterInfo * > Manager::interpreterInfos() const
0235 {
0236     return d->interpreterinfos;
0237 }
0238 
0239 bool Manager::hasInterpreterInfo(const QString &interpretername) const
0240 {
0241     return d->interpreterinfos.contains(interpretername) && d->interpreterinfos[interpretername];
0242 }
0243 
0244 InterpreterInfo *Manager::interpreterInfo(const QString &interpretername) const
0245 {
0246     return hasInterpreterInfo(interpretername) ? d->interpreterinfos[interpretername] : nullptr;
0247 }
0248 
0249 const QString Manager::interpreternameForFile(const QString &file)
0250 {
0251     QRegExp rx;
0252     rx.setPatternSyntax(QRegExp::Wildcard);
0253     for (QHash<QString, InterpreterInfo *>::Iterator it = d->interpreterinfos.begin(); it != d->interpreterinfos.end(); ++it) {
0254         if (! it.value()) {
0255             continue;
0256         }
0257         foreach (const QString &wildcard, it.value()->wildcard().split(' ', Qt::SkipEmptyParts)) {
0258             rx.setPattern(wildcard);
0259             if (rx.exactMatch(file)) {
0260                 return it.value()->interpreterName();
0261             }
0262         }
0263     }
0264     return QString();
0265 }
0266 
0267 Interpreter *Manager::interpreter(const QString &interpretername) const
0268 {
0269     if (! hasInterpreterInfo(interpretername)) {
0270         qCWarning(KROSS_LOG) << "No such interpreter " << interpretername;
0271         return nullptr;
0272     }
0273     return d->interpreterinfos[interpretername]->interpreter();
0274 }
0275 
0276 QStringList Manager::interpreters() const
0277 {
0278     return d->interpreters;
0279 }
0280 
0281 ActionCollection *Manager::actionCollection() const
0282 {
0283     return d->collection;
0284 }
0285 
0286 bool Manager::hasAction(const QString &name)
0287 {
0288     return findChild< Action * >(name) != nullptr;
0289 }
0290 
0291 QObject *Manager::action(const QString &name)
0292 {
0293     Action *action = findChild< Action * >(name);
0294     if (! action) {
0295         action = new Action(this, name);
0296 #if 0
0297         d->actioncollection->insert(action); //FIXME should we really remember the action?
0298 #endif
0299     }
0300     return action;
0301 }
0302 
0303 QObject *Manager::module(const QString &modulename)
0304 {
0305     if (d->modules.contains(modulename)) {
0306         QObject *obj = d->modules[modulename];
0307         if (obj) {
0308             return obj;
0309         }
0310     }
0311 
0312     if (modulename.isEmpty() || modulename.contains(QRegExp("[^a-zA-Z0-9]"))) {
0313         qCWarning(KROSS_LOG) << "Invalid module name " << modulename;
0314         return nullptr;
0315     }
0316 
0317     QByteArray libraryname = QString("krossmodule%1").arg(modulename).toLower().toLatin1();
0318 
0319     if (QFunctionPointer funcPtr = loadLibrary(libraryname.constData(), LibraryFunctionModule)) {
0320         def_module_func func = (def_module_func) funcPtr;
0321         Q_ASSERT(func);
0322         QObject *module = (QObject *)(func)(); // call the function
0323         Q_ASSERT(module);
0324         //krossdebug( QString("Manager::module Module successfully loaded: modulename=%1 module.objectName=%2 module.className=%3").arg(modulename).arg(module->objectName()).arg(module->metaObject()->className()) );
0325         d->modules.insert(modulename, module);
0326         return module;
0327     } else {
0328         qCWarning(KROSS_LOG) << "Failed to load module " << modulename;
0329     }
0330     return nullptr;
0331 }
0332 
0333 void Manager::deleteModules()
0334 {
0335     qDeleteAll(d->modules);
0336     d->modules.clear();
0337 }
0338 
0339 bool Manager::executeScriptFile(const QUrl &file)
0340 {
0341     qCDebug(KROSS_LOG) << "Manager::executeScriptFile() file=" << file.toString();
0342     Action *action = new Action(nullptr /*no parent*/, file);
0343     action->trigger();
0344     bool ok = ! action->hadError();
0345     delete action; //action->delayedDestruct();
0346     return ok;
0347 }
0348 
0349 void Manager::addQObject(QObject *obj, const QString &name)
0350 {
0351     this->addObject(obj, name);
0352 }
0353 
0354 QObject *Manager::qobject(const QString &name) const
0355 {
0356     return this->object(name);
0357 }
0358 
0359 QStringList Manager::qobjectNames() const
0360 {
0361     return this->objects().keys();
0362 }
0363 
0364 MetaTypeHandler *Manager::metaTypeHandler(const QByteArray &typeName) const
0365 {
0366     return d->wrappers.contains(typeName) ? d->wrappers[typeName] : nullptr;
0367 }
0368 
0369 void Manager::registerMetaTypeHandler(const QByteArray &typeName, MetaTypeHandler::FunctionPtr *handler)
0370 {
0371     d->wrappers.insert(typeName, new MetaTypeHandler(handler));
0372 }
0373 
0374 void Manager::registerMetaTypeHandler(const QByteArray &typeName, MetaTypeHandler::FunctionPtr2 *handler)
0375 {
0376     d->wrappers.insert(typeName, new MetaTypeHandler(handler));
0377 }
0378 
0379 void Manager::registerMetaTypeHandler(const QByteArray &typeName, MetaTypeHandler *handler)
0380 {
0381     d->wrappers.insert(typeName, handler);
0382 }
0383 
0384 bool Manager::strictTypesEnabled() const
0385 {
0386     return d->strictTypesEnabled;
0387 }
0388 
0389 void Manager::setStrictTypesEnabled(bool enabled)
0390 {
0391     d->strictTypesEnabled = enabled;
0392 }
0393 
0394 bool Manager::hasHandlerAssigned(const QByteArray &typeName) const
0395 {
0396     return d->wrappers.contains(typeName);
0397 }
0398 
0399 #include "moc_manager.cpp"