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
283 if (oldName == newName) {
284 g_log.warning("Rename: The existing name matches the new name");
285 return;
286 }
287
288 // Make DataService access thread-safe
289 std::unique_lock<std::recursive_mutex> lock(m_mutex);
290
291 auto existingNameIter = datamap.find(oldName);
292 if (existingNameIter == datamap.end()) {
293 lock.unlock();
294 g_log.warning(" rename '" + oldName + "' cannot be found");
295 return;
296 }
297
298 auto existingNameObject = std::move(existingNameIter->second);
299 auto targetNameIter = datamap.find(newName);
300
301 // If we are overriding send a notification for observers
302 if (targetNameIter != datamap.end()) {
303 auto targetNameObject = targetNameIter->second;
304 // As we are renaming the existing name turns into the new name
305 lock.unlock();
306 notificationCenter.postNotification(new BeforeReplaceNotification(newName, targetNameObject, existingNameObject));
307 lock.lock();
308 }
309
310 datamap.erase(existingNameIter);
311
312 if (targetNameIter != datamap.end()) {
313 targetNameIter->second = std::move(existingNameObject);
314 lock.unlock();
315 notificationCenter.postNotification(new AfterReplaceNotification(newName, targetNameIter->second));
316 } else {
317 if (!(datamap.emplace(newName, std::move(existingNameObject)).second)) {
318 // should never happen
319 lock.unlock();
320 std::string error = " add : Unable to insert Data Object : '" + newName + "'";
321 g_log.error(error);
322 throw std::runtime_error(error);
323 } else {
324 lock.unlock();
325 }
326 }
327 g_log.debug("Data Object '" + oldName + "' renamed to '" + newName + "'");
328 notificationCenter.postNotification(new RenameNotification(oldName, newName));
329 }
330
331 //--------------------------------------------------------------------------
333 void clear() {
334 {
335 // Make DataService access thread-safe
336 std::lock_guard<std::recursive_mutex> lock(m_mutex);
337 datamap.clear();
338 }
339 notificationCenter.postNotification(new ClearNotification());
340 g_log.debug() << typeid(this).name() << " cleared.\n";
341 }
342
344 virtual void shutdown() { clear(); }
345
346 //--------------------------------------------------------------------------
349 std::shared_ptr<T> retrieve(const std::string &name) const {
350 // Make DataService access thread-safe
351 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
352
353 auto it = datamap.find(name);
354 if (it != datamap.end()) {
355 return it->second;
356 } else {
357 throw Kernel::Exception::NotFoundError("Unable to find Data Object type with name '" + name + "': data service ",
358 name);
359 }
360 }
361
363 bool doAllWsExist(const std::vector<std::string> &listOfNames) {
364 return std::all_of(listOfNames.cbegin(), listOfNames.cend(),
365 [this](auto const &name) { return this->doesExist(name); });
366 }
367
369 bool doesExist(const std::string &name) const {
370 // Make DataService access thread-safe
371 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
372 auto it = datamap.find(name);
373 return it != datamap.end();
374 }
375
377 size_t size() const {
378 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
379
380 if (showingHiddenObjects()) {
381 return datamap.size();
382 } else {
383 return std::count_if(datamap.cbegin(), datamap.cend(),
384 [](const auto &it) { return !isHiddenDataServiceObject(it.first); });
385 }
386 }
387
397 std::vector<std::string> getObjectNames(DataServiceSort sortState = DataServiceSort::Unsorted,
398 DataServiceHidden hiddenState = DataServiceHidden::Auto,
399 const std::string &contain = "") const {
400
401 std::vector<std::string> foundNames;
402
403 // First test if auto flag is set whether to include hidden
404 if (hiddenState == DataServiceHidden::Auto) {
405 if (showingHiddenObjects()) {
406 hiddenState = DataServiceHidden::Include;
407 } else {
408 hiddenState = DataServiceHidden::Exclude;
409 }
410 }
411
412 // Use the scoping of an if to handle our lock for duration
413 if (hiddenState == DataServiceHidden::Include) {
414 // Getting hidden items
415 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
416 foundNames.reserve(datamap.size());
417 for (const auto &item : datamap) {
418 if (contain.empty()) {
419 foundNames.emplace_back(item.first);
420 } else if (item.first.find(contain) != std::string::npos) {
421 foundNames.emplace_back(item.first);
422 }
423 }
424 // Lock released at end of scope here
425 } else {
426 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
427 foundNames.reserve(datamap.size());
428 for (const auto &item : datamap) {
429 if (!isHiddenDataServiceObject(item.first)) {
430 // This item is not hidden add it
431 if (contain.empty()) {
432 foundNames.emplace_back(item.first);
433 } else if (item.first.find(contain) != std::string::npos) {
434 foundNames.emplace_back(item.first);
435 }
436 }
437 }
438 // Lock released at end of scope here
439 }
440
441 // Now sort if told to
442 if (sortState == DataServiceSort::Sorted) {
443 std::sort(foundNames.begin(), foundNames.end());
444 }
445
446 return foundNames;
447 }
448
450 std::vector<std::shared_ptr<T>> getObjects(DataServiceHidden includeHidden = DataServiceHidden::Auto) const {
451 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
452
453 const bool alwaysIncludeHidden = includeHidden == DataServiceHidden::Include;
454 const bool usingAuto = includeHidden == DataServiceHidden::Auto && showingHiddenObjects();
455
456 const bool showingHidden = alwaysIncludeHidden || usingAuto;
457
458 std::vector<std::shared_ptr<T>> objects;
459 objects.reserve(datamap.size());
460 for (const auto &it : datamap) {
461 if (showingHidden || !isHiddenDataServiceObject(it.first)) {
462 objects.emplace_back(it.second);
463 }
464 }
465 return objects;
466 }
467
468 inline static std::string prefixToHide() { return "__"; }
469
470 inline static bool isHiddenDataServiceObject(const std::string &name) { return name.starts_with(prefixToHide()); }
471
472 static bool showingHiddenObjects() {
473 auto showingHiddenFlag = ConfigService::Instance().getValue<bool>("MantidOptions.InvisibleWorkspaces");
474 return showingHiddenFlag.value_or(false);
475 }
476
481 Poco::NotificationCenter notificationCenter;
483 DataService(const DataService &) = delete;
486
487protected:
489 DataService(const std::string &name) : svcName(name), g_log(svcName) {}
490 virtual ~DataService() = default;
491
492private:
493 void checkForEmptyName(const std::string &name) {
494 if (name.empty()) {
495 const std::string error = "Add Data Object with empty name";
496 g_log.debug() << error << '\n';
497 throw std::runtime_error(error);
498 }
499 }
500
501 void checkForNullPointer(const std::shared_ptr<T> &Tobject) {
502 if (!Tobject) {
503 const std::string error = "Attempt to add empty shared pointer";
504 g_log.debug() << error << '\n';
505 throw std::runtime_error(error);
506 }
507 }
508
511 const std::string svcName;
515 mutable std::recursive_mutex m_mutex;
518}; // End Class Data service
519
520} // Namespace Kernel
521} // 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