Mantid
Loading...
Searching...
No Matches
AlgorithmFactory.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 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//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
11#include "MantidAPI/Algorithm.h"
14#include <algorithm>
15#include <boost/algorithm/string.hpp>
16#include <memory>
17#include <sstream>
18
20
21namespace Mantid::API {
22namespace {
24Kernel::Logger g_log("AlgorithmFactory");
25} // namespace
26
28 // we need to make sure the library manager has been loaded before we
29 // are constructed so that it is destroyed after us and thus does
30 // not close any loaded DLLs with loaded algorithms in them
32 g_log.debug() << "Algorithm Factory created.\n";
33}
34
36
42std::shared_ptr<Algorithm> AlgorithmFactoryImpl::create(const std::string &name, const int &version) const {
43 int local_version = version;
44 // Version not supplied
45 if (version == -1) {
46 local_version = highestVersion(name); // throws if not found
47 }
48
49 // Try create from given name
50 try {
51 return this->createAlgorithm(name, local_version);
53 }
54
55 // Fallback, name might be an alias
56 // Try get real name and create from that instead
57 const auto realName = getRealNameFromAlias(name);
58 if (realName) {
59 // Try create algorithm again with real name
60 try {
61 auto alg = this->createAlgorithm(realName.get(), local_version);
62 alg->calledByAlias = true;
63 return alg;
65 // Get highest registered version
66 const auto hVersion = highestVersion(realName.get()); // Throws if not found
67
68 // The version registered does not match version supplied
69 g_log.error() << "algorithm " << name << " version " << version << " is not registered \n";
70 g_log.error() << "the latest registered version is " << hVersion << '\n';
71 throw std::runtime_error("algorithm not registered " + createName(name, local_version));
72 }
73 } else {
74 throw std::runtime_error("algorithm not registered " + name);
75 }
76}
77
84void AlgorithmFactoryImpl::unsubscribe(const std::string &algorithmName, const int version) {
85 std::string key = this->createName(algorithmName, version);
86 try {
88 // Update version map accordingly
89 auto it = m_vmap.find(algorithmName);
90 if (it != m_vmap.end()) {
91 int highest_version = it->second;
92 if (highest_version > 1 && version == highest_version) // Decrement the highest version
93 {
94 it->second -= 1;
95 } else
96 m_vmap.erase(algorithmName);
97 }
99 g_log.warning() << "Error unsubscribing algorithm " << algorithmName << " version " << version
100 << ". Nothing registered with this name and version.";
101 }
102}
103
110bool AlgorithmFactoryImpl::exists(const std::string &algorithmName, const int version) {
111 if (version == -1) // Find anything
112 {
113 return (m_vmap.find(algorithmName) != m_vmap.end());
114 } else {
115 std::string key = this->createName(algorithmName, version);
117 }
118}
119
125std::string AlgorithmFactoryImpl::createName(const std::string &name, const int &version) const {
126 std::ostringstream oss;
127 oss << name << "|" << version;
128 return (oss.str());
129}
130
135std::pair<std::string, int> AlgorithmFactoryImpl::decodeName(const std::string &mangledName) const {
136 std::string::size_type seperatorPosition = mangledName.find('|');
137 if (seperatorPosition == std::string::npos) {
138 throw std::invalid_argument("Cannot decode a Name string without a \"|\" (bar) character ");
139 }
140 std::string name = mangledName.substr(0, seperatorPosition);
141 int version;
142 std::istringstream ss(mangledName.substr(seperatorPosition + 1));
143 ss >> version;
144
145 g_log.debug() << "mangled string:" << mangledName << " name:" << name << " version:" << version << '\n';
146 return std::pair<std::string, int>(name, version);
147}
148
154const std::vector<std::string> AlgorithmFactoryImpl::getKeys() const {
155 /* We have a separate method rather than just a default argument value
156 to the getKeys(bool) methods so as to avoid an intel compiler warning. */
157
158 // Just call the 'other' getKeys method with the flag set to false
159 return getKeys(false);
160}
161
170const std::vector<std::string> AlgorithmFactoryImpl::getKeys(bool includeHidden) const {
171 // Start with those subscribed with the factory and add the cleanly
172 // constructed algorithm keys
173 std::vector<std::string> names = Kernel::DynamicFactory<Algorithm>::getKeys();
174
175 if (includeHidden) {
176 return names;
177 } else {
178 // hidden categories
179 std::unordered_set<std::string> hiddenCategories;
180 fillHiddenCategories(&hiddenCategories);
181
182 // strip out any algorithms names where all of the categories are hidden
183 std::vector<std::string> validNames;
184 std::vector<std::string>::const_iterator itr_end = names.end();
185 for (std::vector<std::string>::const_iterator itr = names.begin(); itr != itr_end; ++itr) {
186 std::string name = *itr;
187 // check the categories
188 std::pair<std::string, int> namePair = decodeName(name);
189 std::shared_ptr<IAlgorithm> alg = create(namePair.first, namePair.second);
190 std::vector<std::string> categories = alg->categories();
191 bool toBeRemoved = true;
192
193 // for each category
194 std::vector<std::string>::const_iterator itCategoriesEnd = categories.end();
195 for (std::vector<std::string>::const_iterator itCategories = categories.begin(); itCategories != itCategoriesEnd;
196 ++itCategories) {
197 // if the entry is not in the set of hidden categories
198 if (hiddenCategories.find(*itCategories) == hiddenCategories.end()) {
199 toBeRemoved = false;
200 }
201 }
202
203 if (!toBeRemoved) {
204 // just mark them to be removed as we are iterating around the vector
205 // at the moment
206 validNames.emplace_back(name);
207 }
208 }
209 return validNames;
210 }
211}
212
217boost::optional<std::string> AlgorithmFactoryImpl::getRealNameFromAlias(const std::string &alias) const noexcept {
218 auto a_it = m_amap.find(alias);
219 if (a_it == m_amap.end())
220 return boost::none;
221 else
222 return a_it->second;
223}
224
230int AlgorithmFactoryImpl::highestVersion(const std::string &algorithmName) const {
231 auto viter = m_vmap.find(algorithmName);
232 if (viter != m_vmap.end())
233 return viter->second;
234 else {
235 // Fall back, algorithmName might be an alias
236 // Check alias map, then find version from real name
237 const auto realName = getRealNameFromAlias(algorithmName);
238 if (realName != boost::none) {
239 viter = m_vmap.find(realName.get());
240 }
241 if (viter != m_vmap.end())
242 return viter->second;
243 else {
244 throw std::runtime_error("AlgorithmFactory::highestVersion() - Unknown algorithm '" + algorithmName + "'");
245 }
246 }
247}
248
256const std::map<std::string, bool> AlgorithmFactoryImpl::getCategoriesWithState() const {
257 std::map<std::string, bool> resultCategories;
258
259 // hidden categories - empty initially
260 std::unordered_set<std::string> hiddenCategories;
261 fillHiddenCategories(&hiddenCategories);
262
263 // get all of the algorithm keys, including the hidden ones for speed
264 // purposes we will filter later if required
265 std::vector<std::string> names = getKeys(true);
266
267 std::vector<std::string>::const_iterator itr_end = names.end();
268 // for each algorithm
269 for (std::vector<std::string>::const_iterator itr = names.begin(); itr != itr_end; ++itr) {
270 std::string name = *itr;
271 // decode the name and create an instance
272 std::pair<std::string, int> namePair = decodeName(name);
273 std::shared_ptr<IAlgorithm> alg = create(namePair.first, namePair.second);
274 // extract out the categories
275 std::vector<std::string> categories = alg->categories();
276
277 // for each category of the algorithm
278 std::vector<std::string>::const_iterator itCategoriesEnd = categories.end();
279 for (std::vector<std::string>::const_iterator itCategories = categories.begin(); itCategories != itCategoriesEnd;
280 ++itCategories) {
281 bool isHidden = true;
282 // check if the category is hidden
283 if (hiddenCategories.find(*itCategories) == hiddenCategories.end()) {
284 isHidden = false;
285 }
286 resultCategories[*itCategories] = isHidden;
287 }
288 }
289 return resultCategories;
290}
291
300const std::unordered_set<std::string> AlgorithmFactoryImpl::getCategories(bool includeHidden) const {
301 std::unordered_set<std::string> validCategories;
302
303 // get all of the information we need
304 auto categoryMap = getCategoriesWithState();
305
306 // iterate around the map
307 for (auto const &category : categoryMap) {
308 bool isHidden = (category).second;
309 if (includeHidden || (!isHidden)) {
310 validCategories.insert((category).first);
311 }
312 }
313
314 return validCategories;
315}
316
328std::vector<AlgorithmDescriptor> AlgorithmFactoryImpl::getDescriptors(bool includeHidden, bool includeAliases) const {
329 // algorithm names
330 auto sv = getKeys(true);
331
332 // hidden categories
333 std::unordered_set<std::string> hiddenCategories;
334 if (!includeHidden) {
335 fillHiddenCategories(&hiddenCategories);
336 }
337
338 // results vector
339 std::vector<AlgorithmDescriptor> res;
340
341 for (const auto &s : sv) {
342 if (s.empty())
343 continue;
345 size_t i = s.find('|');
346 if (i == std::string::npos) {
347 desc.name = s;
348 desc.version = 1;
349 } else if (i > 0) {
350 desc.name = s.substr(0, i);
351 std::string vers = s.substr(i + 1);
352 desc.version = vers.empty() ? 1 : std::stoi(vers);
353 } else
354 continue;
355
356 std::shared_ptr<IAlgorithm> alg = create(desc.name, desc.version);
357 auto categories = alg->categories();
358 desc.alias = alg->alias();
359
360 // For each category
361 auto itCategoriesEnd = categories.end();
362 for (auto itCategories = categories.begin(); itCategories != itCategoriesEnd; ++itCategories) {
363 desc.category = *itCategories;
364
365 // Let's check if this category or any of its parents are hidden.
366 bool categoryIsHidden = false;
367
368 // Split the category into its components.
369 std::vector<std::string> categoryLayers;
370 boost::split(categoryLayers, desc.category, boost::is_any_of("\\"));
371
372 // Traverse each parent category, working our way from the top down.
373 std::string currentLayer;
374 for (auto &categoryLayer : categoryLayers) {
375 currentLayer.append(categoryLayer);
376
377 if (hiddenCategories.find(currentLayer) != hiddenCategories.end()) {
378 // Category is hidden, no need to check any others.
379 categoryIsHidden = true;
380 break;
381 }
382
383 // Add a separator in case we're going down another layer.
384 currentLayer.append("\\");
385 }
386
387 if (!categoryIsHidden) {
388 res.emplace_back(desc);
389 // Add alias to results if included
390 if (!desc.alias.empty() && includeAliases) {
391 // Avoid adding lowercase aliases to the algorithm tab
392 std::string lowerCaseName = desc.name;
393 std::transform(desc.name.cbegin(), desc.name.cend(), lowerCaseName.begin(),
394 [](unsigned char c) { return std::tolower(c); });
395 if (lowerCaseName != desc.alias) {
396 res.emplace_back(AlgorithmDescriptor{desc.alias, desc.version, desc.category, ""});
397 }
398 }
399 }
400 }
401 }
402 return res;
403}
404
405void AlgorithmFactoryImpl::fillHiddenCategories(std::unordered_set<std::string> *categorySet) const {
406 std::string categoryString = Kernel::ConfigService::Instance().getString("algorithms.categories.hidden");
407 Mantid::Kernel::StringTokenizer tokenizer(categoryString, ";",
410 std::copy(tokenizer.begin(), tokenizer.end(), std::inserter(*categorySet, categorySet->end()));
411}
412
417const std::string AlgorithmFactoryImpl::extractAlgName(const std::shared_ptr<IAlgorithm> &alg) const {
418 return alg->name();
419}
420
425int AlgorithmFactoryImpl::extractAlgVersion(const std::shared_ptr<IAlgorithm> &alg) const { return alg->version(); }
426
431const std::string AlgorithmFactoryImpl::extractAlgAlias(const std::shared_ptr<IAlgorithm> &alg) const {
432 return alg->alias();
433}
434
443std::shared_ptr<Algorithm> AlgorithmFactoryImpl::createAlgorithm(const std::string &name, const int version) const {
445}
446
447} // namespace Mantid::API
VersionMap m_vmap
The map holding the registered class names and their highest versions.
bool exists(const std::string &algorithmName, const int version=-1)
Does an algorithm of the given name and version exist.
std::string createName(const std::string &, const int &) const
creates an algorithm name convolved from an name and version
std::shared_ptr< Algorithm > createAlgorithm(const std::string &name, const int version) const
Create an algorithm object with the specified name.
const std::unordered_set< std::string > getCategories(bool includeHidden=false) const
Get the algorithm categories.
boost::optional< std::string > getRealNameFromAlias(const std::string &alias) const noexcept
Get an algorithms name from the alias map.
void fillHiddenCategories(std::unordered_set< std::string > *categorySet) const
fills a set with the hidden categories
int highestVersion(const std::string &algorithmName) const
Returns the highest version of the algorithm currently registered.
std::vector< AlgorithmDescriptor > getDescriptors(bool includeHidden=false, bool includeAliases=false) const
Returns algorithm descriptors.
void unsubscribe(const std::string &algorithmName, const int version)
Unsubscribe the given algorithm.
AlgorithmFactoryImpl()
Private Constructor for singleton class.
const std::string extractAlgName(const std::shared_ptr< IAlgorithm > &alg) const
Extract the name of an algorithm.
std::pair< std::string, int > decodeName(const std::string &mangledName) const
unmangles the names used as keys into the name and version
int extractAlgVersion(const std::shared_ptr< IAlgorithm > &alg) const
Extract the version of an algorithm.
~AlgorithmFactoryImpl() override
Private Destructor.
const std::string extractAlgAlias(const std::shared_ptr< IAlgorithm > &alg) const
Extract the alias of an algorithm.
const std::vector< std::string > getKeys() const override
Get the algorithm names and version - mangled use decodeName to separate.
std::shared_ptr< Algorithm > create(const std::string &, const int &) const
Creates an instance of an algorithm.
const std::map< std::string, bool > getCategoriesWithState() const
Get the algorithm categories.
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:85
The dynamic factory is a base dynamic factory for serving up objects in response to requests from oth...
void unsubscribe(const std::string &className)
Unregisters the given class and deletes the instantiator for the class.
virtual std::shared_ptr< Base > create(const std::string &className) const
Creates a new instance of the class with the given name.
virtual const std::vector< std::string > getKeys() const
Returns the keys in the map.
bool exists(const std::string &className) const
Returns true if the given class is currently registered.
Exception for when an item is not found in a collection.
Definition: Exception.h:145
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
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Iterator begin()
Iterator referring to first element in the container.
@ TOK_IGNORE_EMPTY
ignore empty tokens
@ TOK_TRIM
remove leading and trailing whitespace from tokens
Iterator end()
Iterator referring to the past-the-end element in the container.
Kernel::Logger g_log("ExperimentInfo")
static logger object
Mantid::Kernel::StringTokenizer tokenizer
Definition: Expression.cpp:17
Structure uniquely describing an algorithm with its name, category and version.
std::string name
Algorithm Name.