Mantid
Loading...
Searching...
No Matches
DataService.h
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2008 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7#pragma once
8
9//----------------------------------------------------------------------
10// Includes
11//----------------------------------------------------------------------
12#ifndef Q_MOC_RUN
13#include <memory>
14#endif
17#include "MantidKernel/Logger.h"
18#include <Poco/Notification.h>
19#include <Poco/NotificationCenter.h>
20#include <mutex>
21
22#ifdef _WIN32
23#define strcasecmp _stricmp
24#else
25#include "Strings.h"
26#endif
27
28namespace Mantid {
29namespace Kernel {
30
38
39// Case-insensitive comparison functor for std::map
41 bool operator()(const std::string &lhs, const std::string &rhs) const {
42 return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
43 }
44};
45
57template <typename T> class DLLExport DataService {
58private:
60 using svcmap = std::map<std::string, std::shared_ptr<T>, CaseInsensitiveCmp>;
62 using svc_it = typename svcmap::iterator;
64 using svc_constit = typename svcmap::const_iterator;
65
66public:
68 class NamedObjectNotification : public Poco::Notification {
69 public:
70 NamedObjectNotification(const std::string &name) : Poco::Notification(), m_name(name) {}
71
73 const std::string &objectName() const { return m_name; }
74
75 private:
76 std::string m_name;
77 };
78
82 public:
84 DataServiceNotification(const std::string &name, const std::shared_ptr<T> &obj)
85 : NamedObjectNotification(name), m_object(obj) {}
88 const std::shared_ptr<T> &object() const { return m_object; }
89
90 private:
91 std::shared_ptr<T> m_object;
92 };
93
97 public:
98 AddNotification(const std::string &name, const std::shared_ptr<T> &obj)
100 };
101
105 public:
115 BeforeReplaceNotification(const std::string &name, const std::shared_ptr<T> &obj, const std::shared_ptr<T> &newObj)
116 : DataServiceNotification(name, obj), m_newObject(newObj), m_oldObject(obj) {}
117 const std::shared_ptr<T> &newObject() const { return m_newObject; }
118 const std::shared_ptr<T> &oldObject() const { return m_oldObject; }
119
120 private:
121 std::shared_ptr<T> m_newObject;
122 std::shared_ptr<T> m_oldObject;
123 };
124
128 public:
135 AfterReplaceNotification(const std::string &name, const std::shared_ptr<T> &newObj)
136 : DataServiceNotification(name, newObj) {}
137 };
138
144 public:
146 PreDeleteNotification(const std::string &name, const std::shared_ptr<T> &obj)
148 };
149
160
169 public:
171 RenameNotification(const std::string &name, const std::string &newName)
172 : NamedObjectNotification(name), m_newName(newName) {}
173
175 const std::string &newObjectName() const { return m_newName; }
176
177 private:
178 std::string m_newName;
179 };
180
181 //--------------------------------------------------------------------------
189 virtual void add(const std::string &name, const std::shared_ptr<T> &Tobject) {
190 checkForEmptyName(name);
191 checkForNullPointer(Tobject);
192
193 bool success = false;
194 {
195 // Make DataService access thread-safe
196 std::lock_guard<std::recursive_mutex> lock(m_mutex);
197 // At the moment, you can't overwrite an object (i.e. pass in a name
198 // that's already in the map with a pointer to a different object).
199 // Also, there's nothing to stop the same object from being added
200 // more than once with different names.
201 success = datamap.insert(std::make_pair(name, Tobject)).second;
202 }
203 if (!success) {
204 std::string error = " add : Unable to insert Data Object : '" + name + "'";
205 g_log.error(error);
206 throw std::runtime_error(error);
207 } else {
208 g_log.debug() << "Add Data Object " << name << " successful\n";
209 notificationCenter.postNotification(new AddNotification(name, Tobject));
210 }
211 }
212
213 //--------------------------------------------------------------------------
221 virtual void addOrReplace(const std::string &name, const std::shared_ptr<T> &Tobject) {
222 checkForNullPointer(Tobject);
223
224 // Make DataService access thread-safe
225 std::unique_lock<std::recursive_mutex> lock(m_mutex);
226
227 // find if the Tobject already exists
228 auto it = datamap.find(name);
229 if (it != datamap.end()) {
230 lock.unlock();
231 g_log.debug("Data Object '" + name + "' replaced in data service.\n");
232
233 notificationCenter.postNotification(new BeforeReplaceNotification(name, it->second, Tobject));
234
235 lock.lock();
236 it->second = Tobject;
237 lock.unlock();
238
239 notificationCenter.postNotification(new AfterReplaceNotification(name, Tobject));
240 } else {
241 // Avoid double-locking
242 lock.unlock();
243 DataService::add(name, Tobject);
244 }
245 }
246
247 //--------------------------------------------------------------------------
250 void remove(const std::string &name) {
251 // Make DataService access thread-safe
252 std::unique_lock<std::recursive_mutex> lock(m_mutex);
253
254 auto it = datamap.find(name);
255 if (it == datamap.end()) {
256 lock.unlock();
257 g_log.debug(" remove '" + name + "' cannot be found");
258 return;
259 }
260 // The map is shared across threads so the item is erased from the map
261 // before unlocking the mutex and is held in a local stack variable.
262 // This protects it from being modified by another thread.
263 auto data = std::move(it->second);
264 datamap.erase(it);
265
266 // Do NOT use "it" iterator after this point. Other threads may modify the
267 // map
268 lock.unlock();
269 notificationCenter.postNotification(new PreDeleteNotification(name, data));
270 data.reset(); // DataService now has no references to the object
271 g_log.debug("Data Object '" + name + "' deleted from data service.");
272 notificationCenter.postNotification(new PostDeleteNotification(name));
273 }
274
275 //--------------------------------------------------------------------------
280 void rename(const std::string &oldName, const std::string &newName) {
281 checkForEmptyName(newName);
282 bool caseInsensitiveMatch = false;
283
284 if (!strcasecmp(oldName.c_str(), newName.c_str())) {
285 if (oldName == newName) {
286 g_log.warning("Rename: The existing name matches the new name");
287 return;
288 }
289 caseInsensitiveMatch = true;
290 }
291 // Make DataService access thread-safe
292 std::unique_lock<std::recursive_mutex> lock(m_mutex);
293
294 auto existingNameIter = datamap.find(oldName);
295 if (existingNameIter == datamap.end()) {
296 lock.unlock();
297 g_log.warning(" rename '" + oldName + "' cannot be found");
298 return;
299 }
300
301 auto existingNameObject = std::move(existingNameIter->second);
302 auto targetNameIter = caseInsensitiveMatch ? datamap.end() : datamap.find(newName);
303
304 // If we are overriding send a notification for observers
305 if (targetNameIter != datamap.end()) {
306 auto targetNameObject = targetNameIter->second;
307 // As we are renaming the existing name turns into the new name
308 lock.unlock();
309 notificationCenter.postNotification(new BeforeReplaceNotification(newName, targetNameObject, existingNameObject));
310 lock.lock();
311 }
312
313 datamap.erase(existingNameIter);
314
315 if (targetNameIter != datamap.end()) {
316 targetNameIter->second = std::move(existingNameObject);
317 lock.unlock();
318 notificationCenter.postNotification(new AfterReplaceNotification(newName, targetNameIter->second));
319 } else {
320 if (!(datamap.emplace(newName, std::move(existingNameObject)).second)) {
321 // should never happen
322 lock.unlock();
323 std::string error = " add : Unable to insert Data Object : '" + newName + "'";
324 g_log.error(error);
325 throw std::runtime_error(error);
326 } else {
327 lock.unlock();
328 }
329 }
330 g_log.debug("Data Object '" + oldName + "' renamed to '" + newName + "'");
331 notificationCenter.postNotification(new RenameNotification(oldName, newName));
332 }
333
334 //--------------------------------------------------------------------------
336 void clear() {
337 {
338 // Make DataService access thread-safe
339 std::lock_guard<std::recursive_mutex> lock(m_mutex);
340 datamap.clear();
341 }
342 notificationCenter.postNotification(new ClearNotification());
343 g_log.debug() << typeid(this).name() << " cleared.\n";
344 }
345
347 virtual void shutdown() { clear(); }
348
349 //--------------------------------------------------------------------------
352 std::shared_ptr<T> retrieve(const std::string &name) const {
353 // Make DataService access thread-safe
354 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
355
356 auto it = datamap.find(name);
357 if (it != datamap.end()) {
358 return it->second;
359 } else {
360 throw Kernel::Exception::NotFoundError("Unable to find Data Object type with name '" + name + "': data service ",
361 name);
362 }
363 }
364
366 bool doAllWsExist(const std::vector<std::string> &listOfNames) {
367 return std::all_of(listOfNames.cbegin(), listOfNames.cend(),
368 [this](auto const &name) { return this->doesExist(name); });
369 }
370
372 bool doesExist(const std::string &name) const {
373 // Make DataService access thread-safe
374 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
375 auto it = datamap.find(name);
376 return it != datamap.end();
377 }
378
380 size_t size() const {
381 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
382
383 if (showingHiddenObjects()) {
384 return datamap.size();
385 } else {
386 return std::count_if(datamap.cbegin(), datamap.cend(),
387 [](const auto &it) { return !isHiddenDataServiceObject(it.first); });
388 }
389 }
390
400 std::vector<std::string> getObjectNames(DataServiceSort sortState = DataServiceSort::Unsorted,
401 DataServiceHidden hiddenState = DataServiceHidden::Auto,
402 const std::string &contain = "") const {
403
404 std::vector<std::string> foundNames;
405
406 // First test if auto flag is set whether to include hidden
407 if (hiddenState == DataServiceHidden::Auto) {
408 if (showingHiddenObjects()) {
409 hiddenState = DataServiceHidden::Include;
410 } else {
411 hiddenState = DataServiceHidden::Exclude;
412 }
413 }
414
415 // Use the scoping of an if to handle our lock for duration
416 if (hiddenState == DataServiceHidden::Include) {
417 // Getting hidden items
418 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
419 foundNames.reserve(datamap.size());
420 for (const auto &item : datamap) {
421 if (contain.empty()) {
422 foundNames.emplace_back(item.first);
423 } else if (item.first.find(contain) != std::string::npos) {
424 foundNames.emplace_back(item.first);
425 }
426 }
427 // Lock released at end of scope here
428 } else {
429 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
430 foundNames.reserve(datamap.size());
431 for (const auto &item : datamap) {
432 if (!isHiddenDataServiceObject(item.first)) {
433 // This item is not hidden add it
434 if (contain.empty()) {
435 foundNames.emplace_back(item.first);
436 } else if (item.first.find(contain) != std::string::npos) {
437 foundNames.emplace_back(item.first);
438 }
439 }
440 }
441 // Lock released at end of scope here
442 }
443
444 // Now sort if told to
445 if (sortState == DataServiceSort::Sorted) {
446 std::sort(foundNames.begin(), foundNames.end());
447 }
448
449 return foundNames;
450 }
451
453 std::vector<std::shared_ptr<T>> getObjects(DataServiceHidden includeHidden = DataServiceHidden::Auto) const {
454 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
455
456 const bool alwaysIncludeHidden = includeHidden == DataServiceHidden::Include;
457 const bool usingAuto = includeHidden == DataServiceHidden::Auto && showingHiddenObjects();
458
459 const bool showingHidden = alwaysIncludeHidden || usingAuto;
460
461 std::vector<std::shared_ptr<T>> objects;
462 objects.reserve(datamap.size());
463 for (const auto &it : datamap) {
464 if (showingHidden || !isHiddenDataServiceObject(it.first)) {
465 objects.emplace_back(it.second);
466 }
467 }
468 return objects;
469 }
470
471 inline static std::string prefixToHide() { return "__"; }
472
473 inline static bool isHiddenDataServiceObject(const std::string &name) { return name.starts_with(prefixToHide()); }
474
475 static bool showingHiddenObjects() {
476 auto showingHiddenFlag = ConfigService::Instance().getValue<bool>("MantidOptions.InvisibleWorkspaces");
477 return showingHiddenFlag.value_or(false);
478 }
479
484 Poco::NotificationCenter notificationCenter;
486 DataService(const DataService &) = delete;
489
490protected:
492 DataService(const std::string &name) : svcName(name), g_log(svcName) {}
493 virtual ~DataService() = default;
494
495private:
496 void checkForEmptyName(const std::string &name) {
497 if (name.empty()) {
498 const std::string error = "Add Data Object with empty name";
499 g_log.debug() << error << '\n';
500 throw std::runtime_error(error);
501 }
502 }
503
504 void checkForNullPointer(const std::shared_ptr<T> &Tobject) {
505 if (!Tobject) {
506 const std::string error = "Attempt to add empty shared pointer";
507 g_log.debug() << error << '\n';
508 throw std::runtime_error(error);
509 }
510 }
511
514 const std::string svcName;
518 mutable std::recursive_mutex m_mutex;
521}; // End Class Data service
522
523} // Namespace Kernel
524} // Namespace Mantid
std::string name
Definition Run.cpp:60
const std::vector< double > & rhs
double error
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
Definition System.h:37
double obj
the value of the quadratic function
AddNotification is sent after an new object is added to the data service.
Definition DataService.h:96
AddNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Definition DataService.h:98
AfterReplaceNotification is sent after an object is replaced in the addOrReplace() function.
AfterReplaceNotification(const std::string &name, const std::shared_ptr< T > &newObj)
Constructor.
BeforeReplaceNotification is sent before an object is replaced in the addOrReplace() function.
const std::shared_ptr< T > & oldObject() const
BeforeReplaceNotification(const std::string &name, const std::shared_ptr< T > &obj, const std::shared_ptr< T > &newObj)
Constructor.
const std::shared_ptr< T > & newObject() const
Returns the pointer to the new object.
std::shared_ptr< T > m_newObject
shared pointer to the object
Clear notification is sent when the service is cleared.
Base class for DataService notifications that also stores a pointer to the object.
Definition DataService.h:81
DataServiceNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Definition DataService.h:84
const std::shared_ptr< T > & object() const
Returns the const pointer to the object concerned or 0 if it is a general notification.
Definition DataService.h:88
std::shared_ptr< T > m_object
shared pointer to the object
Definition DataService.h:91
Class for named object notifications.
Definition DataService.h:68
const std::string & objectName() const
Returns the name of the object.
Definition DataService.h:73
PostDeleteNotification is sent after an object is deleted from the data service.
PostDeleteNotification(const std::string &name)
Constructor.
PreDeleteNotification is sent before an object is deleted from the data service.
PreDeleteNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Rename notification is sent when the rename method is called.
const std::string & newObjectName() const
New name for the object.
RenameNotification(const std::string &name, const std::string &newName)
Constructor.
DataService stores instances of a given type.
Definition DataService.h:57
virtual void add(const std::string &name, const std::shared_ptr< T > &Tobject)
Add an object to the service.
static std::string prefixToHide()
bool doesExist(const std::string &name) const
Check to see if a data object exists in the store.
void checkForNullPointer(const std::shared_ptr< T > &Tobject)
void rename(const std::string &oldName, const std::string &newName)
Rename an object within the service.
std::vector< std::string > getObjectNames(DataServiceSort sortState=DataServiceSort::Unsorted, DataServiceHidden hiddenState=DataServiceHidden::Auto, const std::string &contain="") const
Returns a vector of strings containing all object names in the ADS.
Logger g_log
Logger for this DataService.
svcmap datamap
Map of objects in the data service.
const std::string svcName
DataService name.
virtual void shutdown()
Prepare for shutdown.
std::shared_ptr< T > retrieve(const std::string &name) const
Get a shared pointer to a stored data object.
void checkForEmptyName(const std::string &name)
DataService & operator=(const DataService &)=delete
Deleted copy assignment operator.
size_t size() const
Return the number of objects stored by the data service.
std::map< std::string, std::shared_ptr< T >, CaseInsensitiveCmp > svcmap
Typedef for the map holding the names of and pointers to the data objects.
Definition DataService.h:60
static bool showingHiddenObjects()
virtual ~DataService()=default
std::recursive_mutex m_mutex
Recursive mutex to avoid simultaneous access or notifications.
typename svcmap::const_iterator svc_constit
Const iterator for the data store map.
Definition DataService.h:64
bool doAllWsExist(const std::vector< std::string > &listOfNames)
Checks all elements within the specified vector exist in the ADS.
virtual void addOrReplace(const std::string &name, const std::shared_ptr< T > &Tobject)
Add or replace an object to the service.
typename svcmap::iterator svc_it
Iterator for the data store map.
Definition DataService.h:62
Poco::NotificationCenter notificationCenter
Sends notifications to observers.
DataService(const std::string &name)
Protected constructor (singleton)
std::vector< std::shared_ptr< T > > getObjects(DataServiceHidden includeHidden=DataServiceHidden::Auto) const
Get a vector of the pointers to the data objects stored by the service.
void remove(const std::string &name)
Remove an object from the service.
static bool isHiddenDataServiceObject(const std::string &name)
void clear()
Empty the service.
DataService(const DataService &)=delete
Deleted copy constructor.
Exception for when an item is not found in a collection.
Definition Exception.h:145
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
DataServiceSort
Flag for whether to sort items before returning.
Definition DataService.h:32
DataServiceHidden
Flag for whether to include hidden items when returning, Auto queries the class to determine this beh...
Definition DataService.h:37
Helper class which provides the Collimation Length for SANS instruments.
bool operator()(const std::string &lhs, const std::string &rhs) const
Definition DataService.h:41