/*============================================================================= Library: CTK Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "ctkServices_p.h" #include #include #include #include #include "ctkServiceFactory.h" #include "ctkPluginConstants.h" #include "ctkPluginFrameworkContext_p.h" #include "ctkServiceException.h" #include "ctkServiceRegistrationPrivate.h" #include "ctkLDAPExpr_p.h" //---------------------------------------------------------------------------- struct ServiceRegistrationComparator { bool operator()(const ctkServiceRegistration& a, const ctkServiceRegistration& b) const { return a < b; } }; //---------------------------------------------------------------------------- ctkDictionary ctkServices::createServiceProperties(const ctkDictionary& in, const QStringList& classes, long sid) { static qlonglong nextServiceID = 1; ctkDictionary props = in; if (!classes.isEmpty()) { props.insert(ctkPluginConstants::OBJECTCLASS, classes); } props.insert(ctkPluginConstants::SERVICE_ID, sid != -1 ? sid : nextServiceID++); return props; } //---------------------------------------------------------------------------- ctkServices::ctkServices(ctkPluginFrameworkContext* fwCtx) : mutex(), framework(fwCtx) { } //---------------------------------------------------------------------------- ctkServices::~ctkServices() { clear(); } //---------------------------------------------------------------------------- void ctkServices::clear() { services.clear(); classServices.clear(); framework = 0; } //---------------------------------------------------------------------------- ctkServiceRegistration ctkServices::registerService(ctkPluginPrivate* plugin, const QStringList& classes, QObject* service, const ctkDictionary& properties) { if (service == 0) { throw std::invalid_argument("Can't register 0 as a service"); } // Check if service implements claimed classes and that they exist. for (QStringListIterator i(classes); i.hasNext();) { QString cls = i.next(); if (cls.isEmpty()) { throw std::invalid_argument("Can't register as null class"); } if (!(qobject_cast(service))) { if (!checkServiceClass(service, cls)) { QString msg = QString("Service class %1 is not an instance of %2. Maybe you forgot the Q_INTERFACES macro in the service class.") .arg(service->metaObject()->className()).arg(cls); throw std::invalid_argument(msg.toStdString()); } } } ctkServiceRegistration res(plugin, service, createServiceProperties(properties, classes)); { QMutexLocker lock(&mutex); services.insert(res, classes); for (QStringListIterator i(classes); i.hasNext(); ) { QString currClass = i.next(); QList& s = classServices[currClass]; QList::iterator ip = std::lower_bound(s.begin(), s.end(), res, ServiceRegistrationComparator()); s.insert(ip, res); } } ctkServiceReference r = res.getReference(); plugin->fwCtx->listeners.serviceChanged( plugin->fwCtx->listeners.getMatchingServiceSlots(r), ctkServiceEvent(ctkServiceEvent::REGISTERED, r)); return res; } //---------------------------------------------------------------------------- void ctkServices::updateServiceRegistrationOrder(const ctkServiceRegistration& sr, const QStringList& classes) { QMutexLocker lock(&mutex); for (QStringListIterator i(classes); i.hasNext(); ) { QList& s = classServices[i.next()]; s.removeAll(sr); s.insert(std::lower_bound(s.begin(), s.end(), sr, ServiceRegistrationComparator()), sr); } } //---------------------------------------------------------------------------- bool ctkServices::checkServiceClass(QObject* service, const QString& cls) const { return service->inherits(cls.toAscii()); } //---------------------------------------------------------------------------- QList ctkServices::get(const QString& clazz) const { QMutexLocker lock(&mutex); return classServices.value(clazz); } //---------------------------------------------------------------------------- ctkServiceReference ctkServices::get(ctkPluginPrivate* plugin, const QString& clazz) const { QMutexLocker lock(&mutex); try { QList srs = get_unlocked(clazz, QString(), plugin); if (framework->debug.service_reference) { qDebug() << "get service ref" << clazz << "for plugin" << plugin->location << " = " << srs.size() << "refs"; } if (!srs.isEmpty()) { return srs.front(); } } catch (const std::invalid_argument& ) { } return ctkServiceReference(); } //---------------------------------------------------------------------------- QList ctkServices::get(const QString& clazz, const QString& filter, ctkPluginPrivate* plugin) const { QMutexLocker lock(&mutex); return get_unlocked(clazz, filter, plugin); } //---------------------------------------------------------------------------- QList ctkServices::get_unlocked(const QString& clazz, const QString& filter, ctkPluginPrivate* plugin) const { Q_UNUSED(plugin) QListIterator* s = 0; QList v; ctkLDAPExpr ldap; if (clazz.isEmpty()) { if (!filter.isEmpty()) { ldap = ctkLDAPExpr(filter); QSet matched; if (ldap.getMatchedObjectClasses(matched)) { v.clear(); foreach (QString className, matched) { const QList& cl = classServices[className]; v += cl; } if (!v.isEmpty()) { s = new QListIterator(v); } else { return QList(); } } else { s = new QListIterator(services.keys()); } } else { s = new QListIterator(services.keys()); } } else { QList v = classServices.value(clazz); if (!v.isEmpty()) { s = new QListIterator(v); } else { return QList(); } if (!filter.isEmpty()) { ldap = ctkLDAPExpr(filter); } } QList res; while (s->hasNext()) { ctkServiceRegistration sr = s->next(); ctkServiceReference sri = sr.getReference(); if (filter.isEmpty() || ldap.evaluate(sr.d_func()->properties, false)) { res.push_back(sri); } } delete s; return res; } //---------------------------------------------------------------------------- void ctkServices::removeServiceRegistration(const ctkServiceRegistration& sr) { QMutexLocker lock(&mutex); QStringList classes = sr.d_func()->properties.value(ctkPluginConstants::OBJECTCLASS).toStringList(); services.remove(sr); for (QStringListIterator i(classes); i.hasNext(); ) { QString currClass = i.next(); QList& s = classServices[currClass]; if (s.size() > 1) { s.removeAll(sr); } else { classServices.remove(currClass); } } } //---------------------------------------------------------------------------- QList ctkServices::getRegisteredByPlugin(ctkPluginPrivate* p) const { QMutexLocker lock(&mutex); QList res; for (QHashIterator i(services); i.hasNext(); ) { ctkServiceRegistration sr = i.next().key(); if (sr.d_func()->plugin == p) { res.push_back(sr); } } return res; } //---------------------------------------------------------------------------- QList ctkServices::getUsedByPlugin(QSharedPointer p) const { QMutexLocker lock(&mutex); QList res; for (QHashIterator i(services); i.hasNext(); ) { ctkServiceRegistration sr = i.next().key(); if (sr.d_func()->isUsedByPlugin(p)) { res.push_back(sr); } } return res; }