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
16#include "MantidKernel/DllConfig.h"
18#include "MantidKernel/Logger.h"
19#include <Poco/Notification.h>
20#include <Poco/NotificationCenter.h>
21#include <mutex>
22
23#ifdef _WIN32
24#define strcasecmp _stricmp
25#else
26#include "Strings.h"
27#endif
28
29namespace Mantid {
30namespace Kernel {
31
39
40// Case-insensitive comparison functor for std::map
42 bool operator()(const std::string &lhs, const std::string &rhs) const {
43 return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
44 }
45};
46
58#if !defined(_WIN32)
59template <typename T> class MANTID_KERNEL_DLL DataService {
60#else
61template <typename T> class DataService {
62#endif
63private:
65 using svcmap = std::map<std::string, std::shared_ptr<T>, CaseInsensitiveCmp>;
67 using svc_it = typename svcmap::iterator;
69 using svc_constit = typename svcmap::const_iterator;
70
71public:
73 class NamedObjectNotification : public Poco::Notification {
74 public:
75 NamedObjectNotification(const std::string &name) : Poco::Notification(), m_name(name) {}
76
78 const std::string &objectName() const { return m_name; }
79
80 private:
81 std::string m_name;
82 };
83
87 public:
89 DataServiceNotification(const std::string &name, const std::shared_ptr<T> &obj)
90 : NamedObjectNotification(name), m_object(obj) {}
93 const std::shared_ptr<T> &object() const { return m_object; }
94
95 private:
96 std::shared_ptr<T> m_object;
97 };
98
102 public:
103 AddNotification(const std::string &name, const std::shared_ptr<T> &obj)
105 };
106
110 public:
120 BeforeReplaceNotification(const std::string &name, const std::shared_ptr<T> &obj, const std::shared_ptr<T> &newObj)
121 : DataServiceNotification(name, obj), m_newObject(newObj), m_oldObject(obj) {}
122 const std::shared_ptr<T> &newObject() const { return m_newObject; }
123 const std::shared_ptr<T> &oldObject() const { return m_oldObject; }
124
125 private:
126 std::shared_ptr<T> m_newObject;
127 std::shared_ptr<T> m_oldObject;
128 };
129
133 public:
140 AfterReplaceNotification(const std::string &name, const std::shared_ptr<T> &newObj)
141 : DataServiceNotification(name, newObj) {}
142 };
143
149 public:
151 PreDeleteNotification(const std::string &name, const std::shared_ptr<T> &obj)
153 };
154
165
174 public:
176 RenameNotification(const std::string &name, const std::string &newName)
177 : NamedObjectNotification(name), m_newName(newName) {}
178
180 const std::string &newObjectName() const { return m_newName; }
181
182 private:
183 std::string m_newName;
184 };
185
186 //--------------------------------------------------------------------------
194 virtual void add(const std::string &name, const std::shared_ptr<T> &Tobject) {
195 checkForEmptyName(name);
196 checkForNullPointer(Tobject);
197
198 bool success = false;
199 {
200 // Make DataService access thread-safe
201 std::lock_guard<std::recursive_mutex> lock(m_mutex);
202 // At the moment, you can't overwrite an object (i.e. pass in a name
203 // that's already in the map with a pointer to a different object).
204 // Also, there's nothing to stop the same object from being added
205 // more than once with different names.
206 success = datamap.insert(std::make_pair(name, Tobject)).second;
207 }
208 if (!success) {
209 std::string error = " add : Unable to insert Data Object : '" + name + "'";
210 g_log.error(error);
211 throw std::runtime_error(error);
212 } else {
213 g_log.debug() << "Add Data Object " << name << " successful\n";
214 notificationCenter.postNotification(new AddNotification(name, Tobject));
215 }
216 }
217
218 //--------------------------------------------------------------------------
226 virtual void addOrReplace(const std::string &name, const std::shared_ptr<T> &Tobject) {
227 checkForNullPointer(Tobject);
228
229 // Make DataService access thread-safe
230 std::unique_lock<std::recursive_mutex> lock(m_mutex);
231
232 // find if the Tobject already exists
233 auto it = datamap.find(name);
234 if (it != datamap.end()) {
235 lock.unlock();
236 g_log.debug("Data Object '" + name + "' replaced in data service.\n");
237
238 notificationCenter.postNotification(new BeforeReplaceNotification(name, it->second, Tobject));
239
240 lock.lock();
241 it->second = Tobject;
242 lock.unlock();
243
244 notificationCenter.postNotification(new AfterReplaceNotification(name, Tobject));
245 } else {
246 // Avoid double-locking
247 lock.unlock();
248 DataService::add(name, Tobject);
249 }
250 }
251
252 //--------------------------------------------------------------------------
255 void remove(const std::string &name) {
256 // Make DataService access thread-safe
257 std::unique_lock<std::recursive_mutex> lock(m_mutex);
258
259 auto it = datamap.find(name);
260 if (it == datamap.end()) {
261 lock.unlock();
262 g_log.debug(" remove '" + name + "' cannot be found");
263 return;
264 }
265 // The map is shared across threads so the item is erased from the map
266 // before unlocking the mutex and is held in a local stack variable.
267 // This protects it from being modified by another thread.
268 auto data = std::move(it->second);
269 datamap.erase(it);
270
271 // Do NOT use "it" iterator after this point. Other threads may modify the
272 // map
273 lock.unlock();
274 notificationCenter.postNotification(new PreDeleteNotification(name, data));
275 data.reset(); // DataService now has no references to the object
276 g_log.debug("Data Object '" + name + "' deleted from data service.");
277 notificationCenter.postNotification(new PostDeleteNotification(name));
278 }
279
280 //--------------------------------------------------------------------------
285 void rename(const std::string &oldName, const std::string &newName) {
286 checkForEmptyName(newName);
287 bool caseInsensitiveMatch = false;
288
289 if (!strcasecmp(oldName.c_str(), newName.c_str())) {
290 if (oldName == newName) {
291 g_log.warning("Rename: The existing name matches the new name");
292 return;
293 }
294 caseInsensitiveMatch = true;
295 }
296 // Make DataService access thread-safe
297 std::unique_lock<std::recursive_mutex> lock(m_mutex);
298
299 auto existingNameIter = datamap.find(oldName);
300 if (existingNameIter == datamap.end()) {
301 lock.unlock();
302 g_log.warning(" rename '" + oldName + "' cannot be found");
303 return;
304 }
305
306 auto existingNameObject = std::move(existingNameIter->second);
307 auto targetNameIter = caseInsensitiveMatch ? datamap.end() : datamap.find(newName);
308
309 // If we are overriding send a notification for observers
310 if (targetNameIter != datamap.end()) {
311 auto targetNameObject = targetNameIter->second;
312 // As we are renaming the existing name turns into the new name
313 lock.unlock();
314 notificationCenter.postNotification(new BeforeReplaceNotification(newName, targetNameObject, existingNameObject));
315 lock.lock();
316 }
317
318 datamap.erase(existingNameIter);
319
320 if (targetNameIter != datamap.end()) {
321 targetNameIter->second = std::move(existingNameObject);
322 lock.unlock();
323 notificationCenter.postNotification(new AfterReplaceNotification(newName, targetNameIter->second));
324 } else {
325 if (!(datamap.emplace(newName, std::move(existingNameObject)).second)) {
326 // should never happen
327 lock.unlock();
328 std::string error = " add : Unable to insert Data Object : '" + newName + "'";
329 g_log.error(error);
330 throw std::runtime_error(error);
331 } else {
332 lock.unlock();
333 }
334 }
335 g_log.debug("Data Object '" + oldName + "' renamed to '" + newName + "'");
336 notificationCenter.postNotification(new RenameNotification(oldName, newName));
337 }
338
339 //--------------------------------------------------------------------------
341 void clear() {
342 {
343 // Make DataService access thread-safe
344 std::lock_guard<std::recursive_mutex> lock(m_mutex);
345 datamap.clear();
346 }
347 notificationCenter.postNotification(new ClearNotification());
348 g_log.debug() << typeid(this).name() << " cleared.\n";
349 }
350
352 virtual void shutdown() { clear(); }
353
354 //--------------------------------------------------------------------------
357 std::shared_ptr<T> retrieve(const std::string &name) const {
358 // Make DataService access thread-safe
359 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
360
361 auto it = datamap.find(name);
362 if (it != datamap.end()) {
363 return it->second;
364 } else {
365 throw Kernel::Exception::NotFoundError("Unable to find Data Object type with name '" + name + "': data service ",
366 name);
367 }
368 }
369
371 bool doAllWsExist(const std::vector<std::string> &listOfNames) {
372 return std::all_of(listOfNames.cbegin(), listOfNames.cend(),
373 [this](auto const &name) { return this->doesExist(name); });
374 }
375
377 bool doesExist(const std::string &name) const {
378 // Make DataService access thread-safe
379 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
380 auto it = datamap.find(name);
381 return it != datamap.end();
382 }
383
385 size_t size() const {
386 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
387
388 if (showingHiddenObjects()) {
389 return datamap.size();
390 } else {
391 return std::count_if(datamap.cbegin(), datamap.cend(),
392 [](const auto &it) { return !isHiddenDataServiceObject(it.first); });
393 }
394 }
395
405 std::vector<std::string> getObjectNames(DataServiceSort sortState = DataServiceSort::Unsorted,
406 DataServiceHidden hiddenState = DataServiceHidden::Auto,
407 const std::string &contain = "") const {
408
409 std::vector<std::string> foundNames;
410
411 // First test if auto flag is set whether to include hidden
412 if (hiddenState == DataServiceHidden::Auto) {
413 if (showingHiddenObjects()) {
414 hiddenState = DataServiceHidden::Include;
415 } else {
416 hiddenState = DataServiceHidden::Exclude;
417 }
418 }
419
420 // Use the scoping of an if to handle our lock for duration
421 if (hiddenState == DataServiceHidden::Include) {
422 // Getting hidden items
423 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
424 foundNames.reserve(datamap.size());
425 for (const auto &item : datamap) {
426 if (contain.empty()) {
427 foundNames.emplace_back(item.first);
428 } else if (item.first.find(contain) != std::string::npos) {
429 foundNames.emplace_back(item.first);
430 }
431 }
432 // Lock released at end of scope here
433 } else {
434 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
435 foundNames.reserve(datamap.size());
436 for (const auto &item : datamap) {
437 if (!isHiddenDataServiceObject(item.first)) {
438 // This item is not hidden add it
439 if (contain.empty()) {
440 foundNames.emplace_back(item.first);
441 } else if (item.first.find(contain) != std::string::npos) {
442 foundNames.emplace_back(item.first);
443 }
444 }
445 }
446 // Lock released at end of scope here
447 }
448
449 // Now sort if told to
450 if (sortState == DataServiceSort::Sorted) {
451 std::sort(foundNames.begin(), foundNames.end());
452 }
453
454 return foundNames;
455 }
456
458 std::vector<std::shared_ptr<T>> getObjects(DataServiceHidden includeHidden = DataServiceHidden::Auto) const {
459 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
460
461 const bool alwaysIncludeHidden = includeHidden == DataServiceHidden::Include;
462 const bool usingAuto = includeHidden == DataServiceHidden::Auto && showingHiddenObjects();
463
464 const bool showingHidden = alwaysIncludeHidden || usingAuto;
465
466 std::vector<std::shared_ptr<T>> objects;
467 objects.reserve(datamap.size());
468 for (const auto &it : datamap) {
469 if (showingHidden || !isHiddenDataServiceObject(it.first)) {
470 objects.emplace_back(it.second);
471 }
472 }
473 return objects;
474 }
475
476 inline static std::string prefixToHide() { return "__"; }
477
478 inline static bool isHiddenDataServiceObject(const std::string &name) { return name.starts_with(prefixToHide()); }
479
480 static bool showingHiddenObjects() {
481 auto showingHiddenFlag = ConfigService::Instance().getValue<bool>("MantidOptions.InvisibleWorkspaces");
482 return showingHiddenFlag.value_or(false);
483 }
484
489 Poco::NotificationCenter notificationCenter;
491 DataService(const DataService &) = delete;
494
495protected:
497 DataService(const std::string &name) : svcName(name), g_log(svcName) {}
498 virtual ~DataService() = default;
499
500private:
501 void checkForEmptyName(const std::string &name) {
502 if (name.empty()) {
503 const std::string error = "Add Data Object with empty name";
504 g_log.debug() << error << '\n';
505 throw std::runtime_error(error);
506 }
507 }
508
509 void checkForNullPointer(const std::shared_ptr<T> &Tobject) {
510 if (!Tobject) {
511 const std::string error = "Attempt to add empty shared pointer";
512 g_log.debug() << error << '\n';
513 throw std::runtime_error(error);
514 }
515 }
516
519 const std::string svcName;
523 mutable std::recursive_mutex m_mutex;
526}; // End Class Data service
527
528} // Namespace Kernel
529} // Namespace Mantid
std::string name
Definition Run.cpp:60
const std::vector< double > & rhs
double error
double obj
the value of the quadratic function
AddNotification is sent after an new object is added to the data service.
AddNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
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:86
DataServiceNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Definition DataService.h:89
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:93
std::shared_ptr< T > m_object
shared pointer to the object
Definition DataService.h:96
Class for named object notifications.
Definition DataService.h:73
const std::string & objectName() const
Returns the name of the object.
Definition DataService.h:78
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:59
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:65
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:69
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:67
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:33
DataServiceHidden
Flag for whether to include hidden items when returning, Auto queries the class to determine this beh...
Definition DataService.h:38
Helper class which provides the Collimation Length for SANS instruments.
bool operator()(const std::string &lhs, const std::string &rhs) const
Definition DataService.h:42