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 <boost/algorithm/string.hpp>
14#include <memory>
15#endif
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
58template <typename T> class DLLExport DataService {
59private:
61 using svcmap = std::map<std::string, std::shared_ptr<T>, CaseInsensitiveCmp>;
63 using svc_it = typename svcmap::iterator;
65 using svc_constit = typename svcmap::const_iterator;
66
67public:
69 class NamedObjectNotification : public Poco::Notification {
70 public:
71 NamedObjectNotification(const std::string &name) : Poco::Notification(), m_name(name) {}
72
74 const std::string &objectName() const { return m_name; }
75
76 private:
77 std::string m_name;
78 };
79
83 public:
85 DataServiceNotification(const std::string &name, const std::shared_ptr<T> &obj)
86 : NamedObjectNotification(name), m_object(obj) {}
89 const std::shared_ptr<T> &object() const { return m_object; }
90
91 private:
92 std::shared_ptr<T> m_object;
93 };
94
98 public:
99 AddNotification(const std::string &name, const std::shared_ptr<T> &obj)
100 : DataServiceNotification(name, obj) {}
101 };
102
106 public:
116 BeforeReplaceNotification(const std::string &name, const std::shared_ptr<T> &obj, const std::shared_ptr<T> &newObj)
117 : DataServiceNotification(name, obj), m_newObject(newObj), m_oldObject(obj) {}
118 const std::shared_ptr<T> &newObject() const { return m_newObject; }
119 const std::shared_ptr<T> &oldObject() const { return m_oldObject; }
120
121 private:
122 std::shared_ptr<T> m_newObject;
123 std::shared_ptr<T> m_oldObject;
124 };
125
129 public:
136 AfterReplaceNotification(const std::string &name, const std::shared_ptr<T> &newObj)
137 : DataServiceNotification(name, newObj) {}
138 };
139
145 public:
147 PreDeleteNotification(const std::string &name, const std::shared_ptr<T> &obj)
148 : DataServiceNotification(name, obj) {}
149 };
150
157 public:
159 PostDeleteNotification(const std::string &name) : NamedObjectNotification(name) {}
160 };
161
164 public:
167 };
170 public:
172 RenameNotification(const std::string &name, const std::string &newName)
173 : NamedObjectNotification(name), m_newName(newName) {}
174
176 const std::string &newObjectName() const { return m_newName; }
177
178 private:
179 std::string m_newName;
180 };
181
182 //--------------------------------------------------------------------------
190 virtual void add(const std::string &name, const std::shared_ptr<T> &Tobject) {
191 checkForEmptyName(name);
192 checkForNullPointer(Tobject);
193
194 bool success = false;
195 {
196 // Make DataService access thread-safe
197 std::lock_guard<std::recursive_mutex> lock(m_mutex);
198 // At the moment, you can't overwrite an object (i.e. pass in a name
199 // that's already in the map with a pointer to a different object).
200 // Also, there's nothing to stop the same object from being added
201 // more than once with different names.
202 success = datamap.insert(std::make_pair(name, Tobject)).second;
203 }
204 if (!success) {
205 std::string error = " add : Unable to insert Data Object : '" + name + "'";
207 throw std::runtime_error(error);
208 } else {
209 g_log.debug() << "Add Data Object " << name << " successful\n";
210 notificationCenter.postNotification(new AddNotification(name, Tobject));
211 }
212 }
213
214 //--------------------------------------------------------------------------
222 virtual void addOrReplace(const std::string &name, const std::shared_ptr<T> &Tobject) {
223 checkForNullPointer(Tobject);
224
225 // Make DataService access thread-safe
226 std::unique_lock<std::recursive_mutex> lock(m_mutex);
227
228 // find if the Tobject already exists
229 auto it = datamap.find(name);
230 if (it != datamap.end()) {
231 lock.unlock();
232 g_log.debug("Data Object '" + name + "' replaced in data service.\n");
233
234 notificationCenter.postNotification(new BeforeReplaceNotification(name, it->second, Tobject));
235
236 lock.lock();
237 it->second = Tobject;
238 lock.unlock();
239
240 notificationCenter.postNotification(new AfterReplaceNotification(name, Tobject));
241 } else {
242 // Avoid double-locking
243 lock.unlock();
244 DataService::add(name, Tobject);
245 }
246 }
247
248 //--------------------------------------------------------------------------
251 void remove(const std::string &name) {
252 // Make DataService access thread-safe
253 std::unique_lock<std::recursive_mutex> lock(m_mutex);
254
255 auto it = datamap.find(name);
256 if (it == datamap.end()) {
257 lock.unlock();
258 g_log.debug(" remove '" + name + "' cannot be found");
259 return;
260 }
261 // The map is shared across threads so the item is erased from the map
262 // before unlocking the mutex and is held in a local stack variable.
263 // This protects it from being modified by another thread.
264 auto data = std::move(it->second);
265 datamap.erase(it);
266
267 // Do NOT use "it" iterator after this point. Other threads may modify the
268 // map
269 lock.unlock();
270 notificationCenter.postNotification(new PreDeleteNotification(name, data));
271 data.reset(); // DataService now has no references to the object
272 g_log.debug("Data Object '" + name + "' deleted from data service.");
273 notificationCenter.postNotification(new PostDeleteNotification(name));
274 }
275
276 //--------------------------------------------------------------------------
281 void rename(const std::string &oldName, const std::string &newName) {
282 checkForEmptyName(newName);
283
284 if (oldName == newName) {
285 g_log.warning("Rename: The existing name matches the new name");
286 return;
287 }
288
289 // Make DataService access thread-safe
290 std::unique_lock<std::recursive_mutex> lock(m_mutex);
291
292 auto existingNameIter = datamap.find(oldName);
293 if (existingNameIter == datamap.end()) {
294 lock.unlock();
295 g_log.warning(" rename '" + oldName + "' cannot be found");
296 return;
297 }
298
299 auto existingNameObject = std::move(existingNameIter->second);
300 auto targetNameIter = datamap.find(newName);
301
302 // If we are overriding send a notification for observers
303 if (targetNameIter != datamap.end()) {
304 auto targetNameObject = targetNameIter->second;
305 // As we are renaming the existing name turns into the new name
306 lock.unlock();
307 notificationCenter.postNotification(new BeforeReplaceNotification(newName, targetNameObject, existingNameObject));
308 lock.lock();
309 }
310
311 datamap.erase(existingNameIter);
312
313 if (targetNameIter != datamap.end()) {
314 targetNameIter->second = std::move(existingNameObject);
315 lock.unlock();
316 notificationCenter.postNotification(new AfterReplaceNotification(newName, targetNameIter->second));
317 } else {
318 if (!(datamap.emplace(newName, std::move(existingNameObject)).second)) {
319 // should never happen
320 lock.unlock();
321 std::string error = " add : Unable to insert Data Object : '" + newName + "'";
323 throw std::runtime_error(error);
324 } else {
325 lock.unlock();
326 }
327 }
328 g_log.debug("Data Object '" + oldName + "' renamed to '" + newName + "'");
329 notificationCenter.postNotification(new RenameNotification(oldName, newName));
330 }
331
332 //--------------------------------------------------------------------------
334 void clear() {
335 {
336 // Make DataService access thread-safe
337 std::lock_guard<std::recursive_mutex> lock(m_mutex);
338 datamap.clear();
339 }
340 notificationCenter.postNotification(new ClearNotification());
341 g_log.debug() << typeid(this).name() << " cleared.\n";
342 }
343
345 virtual void shutdown() { clear(); }
346
347 //--------------------------------------------------------------------------
350 std::shared_ptr<T> retrieve(const std::string &name) const {
351 // Make DataService access thread-safe
352 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
353
354 auto it = datamap.find(name);
355 if (it != datamap.end()) {
356 return it->second;
357 } else {
358 throw Kernel::Exception::NotFoundError("Unable to find Data Object type with name '" + name + "': data service ",
359 name);
360 }
361 }
362
364 bool doAllWsExist(const std::vector<std::string> &listOfNames) {
365 return std::all_of(listOfNames.cbegin(), listOfNames.cend(),
366 [this](auto const &name) { return this->doesExist(name); });
367 }
368
370 bool doesExist(const std::string &name) const {
371 // Make DataService access thread-safe
372 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
373 auto it = datamap.find(name);
374 return it != datamap.end();
375 }
376
378 size_t size() const {
379 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
380
381 if (showingHiddenObjects()) {
382 return datamap.size();
383 } else {
384 return std::count_if(datamap.cbegin(), datamap.cend(),
385 [](const auto &it) { return !isHiddenDataServiceObject(it.first); });
386 }
387 }
388
398 std::vector<std::string> getObjectNames(DataServiceSort sortState = DataServiceSort::Unsorted,
399 DataServiceHidden hiddenState = DataServiceHidden::Auto,
400 const std::string &contain = "") const {
401
402 std::vector<std::string> foundNames;
403
404 // First test if auto flag is set whether to include hidden
405 if (hiddenState == DataServiceHidden::Auto) {
406 if (showingHiddenObjects()) {
407 hiddenState = DataServiceHidden::Include;
408 } else {
409 hiddenState = DataServiceHidden::Exclude;
410 }
411 }
412
413 // Use the scoping of an if to handle our lock for duration
414 if (hiddenState == DataServiceHidden::Include) {
415 // Getting hidden items
416 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
417 foundNames.reserve(datamap.size());
418 for (const auto &item : datamap) {
419 if (contain.empty()) {
420 foundNames.emplace_back(item.first);
421 } else if (item.first.find(contain) != std::string::npos) {
422 foundNames.emplace_back(item.first);
423 }
424 }
425 // Lock released at end of scope here
426 } else {
427 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
428 foundNames.reserve(datamap.size());
429 for (const auto &item : datamap) {
430 if (!isHiddenDataServiceObject(item.first)) {
431 // This item is not hidden add it
432 if (contain.empty()) {
433 foundNames.emplace_back(item.first);
434 } else if (item.first.find(contain) != std::string::npos) {
435 foundNames.emplace_back(item.first);
436 }
437 }
438 }
439 // Lock released at end of scope here
440 }
441
442 // Now sort if told to
443 if (sortState == DataServiceSort::Sorted) {
444 std::sort(foundNames.begin(), foundNames.end());
445 }
446
447 return foundNames;
448 }
449
451 std::vector<std::shared_ptr<T>> getObjects(DataServiceHidden includeHidden = DataServiceHidden::Auto) const {
452 std::lock_guard<std::recursive_mutex> _lock(m_mutex);
453
454 const bool alwaysIncludeHidden = includeHidden == DataServiceHidden::Include;
455 const bool usingAuto = includeHidden == DataServiceHidden::Auto && showingHiddenObjects();
456
457 const bool showingHidden = alwaysIncludeHidden || usingAuto;
458
459 std::vector<std::shared_ptr<T>> objects;
460 objects.reserve(datamap.size());
461 for (const auto &it : datamap) {
462 if (showingHidden || !isHiddenDataServiceObject(it.first)) {
463 objects.emplace_back(it.second);
464 }
465 }
466 return objects;
467 }
468
469 inline static std::string prefixToHide() { return "__"; }
470
471 inline static bool isHiddenDataServiceObject(const std::string &name) {
472 return boost::starts_with(name, prefixToHide());
473 }
474
475 static bool showingHiddenObjects() {
476 auto showingHiddenFlag = ConfigService::Instance().getValue<bool>("MantidOptions.InvisibleWorkspaces");
477 return showingHiddenFlag.get_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
const std::vector< double > & rhs
double error
Definition: IndexPeaks.cpp:133
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
Definition: System.h:53
double obj
the value of the quadratic function
AddNotification is sent after an new object is added to the data service.
Definition: DataService.h:97
AddNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Definition: DataService.h:99
AfterReplaceNotification is sent after an object is replaced in the addOrReplace() function.
Definition: DataService.h:128
AfterReplaceNotification(const std::string &name, const std::shared_ptr< T > &newObj)
Constructor.
Definition: DataService.h:136
BeforeReplaceNotification is sent before an object is replaced in the addOrReplace() function.
Definition: DataService.h:105
const std::shared_ptr< T > & oldObject() const
Definition: DataService.h:119
BeforeReplaceNotification(const std::string &name, const std::shared_ptr< T > &obj, const std::shared_ptr< T > &newObj)
Constructor.
Definition: DataService.h:116
const std::shared_ptr< T > & newObject() const
Returns the pointer to the new object.
Definition: DataService.h:118
std::shared_ptr< T > m_newObject
shared pointer to the object
Definition: DataService.h:122
Clear notification is sent when the service is cleared.
Definition: DataService.h:163
Base class for DataService notifications that also stores a pointer to the object.
Definition: DataService.h:82
DataServiceNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Definition: DataService.h:85
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:89
std::shared_ptr< T > m_object
shared pointer to the object
Definition: DataService.h:92
Class for named object notifications.
Definition: DataService.h:69
const std::string & objectName() const
Returns the name of the object.
Definition: DataService.h:74
PostDeleteNotification is sent after an object is deleted from the data service.
Definition: DataService.h:156
PostDeleteNotification(const std::string &name)
Constructor.
Definition: DataService.h:159
PreDeleteNotification is sent before an object is deleted from the data service.
Definition: DataService.h:144
PreDeleteNotification(const std::string &name, const std::shared_ptr< T > &obj)
Constructor.
Definition: DataService.h:147
Rename notification is sent when the rename method is called.
Definition: DataService.h:169
const std::string & newObjectName() const
New name for the object.
Definition: DataService.h:176
RenameNotification(const std::string &name, const std::string &newName)
Constructor.
Definition: DataService.h:172
DataService stores instances of a given type.
Definition: DataService.h:58
virtual void add(const std::string &name, const std::shared_ptr< T > &Tobject)
Add an object to the service.
Definition: DataService.h:190
static std::string prefixToHide()
Definition: DataService.h:469
bool doesExist(const std::string &name) const
Check to see if a data object exists in the store.
Definition: DataService.h:370
void checkForNullPointer(const std::shared_ptr< T > &Tobject)
Definition: DataService.h:504
void rename(const std::string &oldName, const std::string &newName)
Rename an object within the service.
Definition: DataService.h:281
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.
Definition: DataService.h:398
Logger g_log
Logger for this DataService.
Definition: DataService.h:520
svcmap datamap
Map of objects in the data service.
Definition: DataService.h:516
const std::string svcName
DataService name.
Definition: DataService.h:514
virtual void shutdown()
Prepare for shutdown.
Definition: DataService.h:345
std::shared_ptr< T > retrieve(const std::string &name) const
Get a shared pointer to a stored data object.
Definition: DataService.h:350
void checkForEmptyName(const std::string &name)
Definition: DataService.h:496
DataService & operator=(const DataService &)=delete
Deleted copy assignment operator.
size_t size() const
Return the number of objects stored by the data service.
Definition: DataService.h:378
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:61
static bool showingHiddenObjects()
Definition: DataService.h:475
virtual ~DataService()=default
std::recursive_mutex m_mutex
Recursive mutex to avoid simultaneous access or notifications.
Definition: DataService.h:518
typename svcmap::const_iterator svc_constit
Const iterator for the data store map.
Definition: DataService.h:65
bool doAllWsExist(const std::vector< std::string > &listOfNames)
Checks all elements within the specified vector exist in the ADS.
Definition: DataService.h:364
virtual void addOrReplace(const std::string &name, const std::shared_ptr< T > &Tobject)
Add or replace an object to the service.
Definition: DataService.h:222
typename svcmap::iterator svc_it
Iterator for the data store map.
Definition: DataService.h:63
Poco::NotificationCenter notificationCenter
Sends notifications to observers.
Definition: DataService.h:484
DataService(const std::string &name)
Protected constructor (singleton)
Definition: DataService.h:492
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.
Definition: DataService.h:451
void remove(const std::string &name)
Remove an object from the service.
Definition: DataService.h:251
static bool isHiddenDataServiceObject(const std::string &name)
Definition: DataService.h:471
void clear()
Empty the service.
Definition: DataService.h:334
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:52
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
Kernel::Logger g_log("ExperimentInfo")
static logger object
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.
Definition: Algorithm.h:30
bool operator()(const std::string &lhs, const std::string &rhs) const
Definition: DataService.h:42