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
31 Mantid::Kernel::LibraryManager::Instance();
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 if (const auto realNameAndVersion = getRealNameFromAlias(name)) {
58 // Try create algorithm again with real name
59 try {
60 auto alg = this->createAlgorithm(realNameAndVersion->first, local_version);
61 alg->calledByAlias = true;
62 return alg;
64 // Get highest registered version
65 const auto hVersion = highestVersion(realNameAndVersion->first); // Throws if not found
66
67 // The version registered does not match version supplied
68 g_log.error() << "algorithm " << name << " version " << version << " is not registered \n";
69 g_log.error() << "the latest registered version is " << hVersion << '\n';
70 throw std::runtime_error("algorithm not registered " + createName(name, local_version));
71 }
72 } else {
73 throw std::runtime_error("algorithm not registered " + name);
74 }
75}
76
83void AlgorithmFactoryImpl::unsubscribe(const std::string &algorithmName, const int version) {
84 std::string key = this->createName(algorithmName, version);
85 try {
87 // Update version map accordingly
88 auto it = m_vmap.find(algorithmName);
89 if (it != m_vmap.end()) {
90 int highest_version = it->second;
91 if (highest_version > 1 && version == highest_version) // Decrement the highest version
92 {
93 it->second -= 1;
94 } else
95 m_vmap.erase(algorithmName);
96 }
98 g_log.warning() << "Error unsubscribing algorithm " << algorithmName << " version " << version
99 << ". Nothing registered with this name and version.";
100 }
101}
102
109bool AlgorithmFactoryImpl::exists(const std::string &algorithmName, const int version) {
110 if (version == -1) // Find anything
111 {
112 return (m_vmap.find(algorithmName) != m_vmap.end());
113 } else {
114 std::string key = this->createName(algorithmName, version);
116 }
117}
118
124std::string AlgorithmFactoryImpl::createName(const std::string &name, const int &version) const {
125 std::ostringstream oss;
126 oss << name << "|" << version;
127 return (oss.str());
128}
129
134std::pair<std::string, int> AlgorithmFactoryImpl::decodeName(const std::string &mangledName) const {
135 std::string::size_type seperatorPosition = mangledName.find('|');
136 if (seperatorPosition == std::string::npos) {
137 throw std::invalid_argument("Cannot decode a Name string without a \"|\" (bar) character ");
138 }
139 std::string name = mangledName.substr(0, seperatorPosition);
140 int version;
141 std::istringstream ss(mangledName.substr(seperatorPosition + 1));
142 ss >> version;
143
144 g_log.debug() << "mangled string:" << mangledName << " name:" << name << " version:" << version << '\n';
145 return std::pair<std::string, int>(name, version);
146}
147
153const std::vector<std::string> AlgorithmFactoryImpl::getKeys() const {
154 /* We have a separate method rather than just a default argument value
155 to the getKeys(bool) methods so as to avoid an intel compiler warning. */
156
157 // Just call the 'other' getKeys method with the flag set to false
158 return getKeys(false);
159}
160
169const std::vector<std::string> AlgorithmFactoryImpl::getKeys(bool includeHidden) const {
170 // Start with those subscribed with the factory and add the cleanly
171 // constructed algorithm keys
172 std::vector<std::string> names = Kernel::DynamicFactory<Algorithm>::getKeys();
173
174 if (includeHidden) {
175 return names;
176 } else {
177 // hidden categories
178 std::unordered_set<std::string> hiddenCategories;
179 fillHiddenCategories(&hiddenCategories);
180
181 // strip out any algorithms names where all of the categories are hidden
182 std::vector<std::string> validNames;
183 std::vector<std::string>::const_iterator itr_end = names.end();
184 for (std::vector<std::string>::const_iterator itr = names.begin(); itr != itr_end; ++itr) {
185 std::string name = *itr;
186 // check the categories
187 std::pair<std::string, int> namePair = decodeName(name);
188 std::shared_ptr<IAlgorithm> alg = create(namePair.first, namePair.second);
189 std::vector<std::string> categories = alg->categories();
190 bool toBeRemoved = true;
191
192 // for each category
193 std::vector<std::string>::const_iterator itCategoriesEnd = categories.end();
194 for (std::vector<std::string>::const_iterator itCategories = categories.begin(); itCategories != itCategoriesEnd;
195 ++itCategories) {
196 // if the entry is not in the set of hidden categories
197 if (hiddenCategories.find(*itCategories) == hiddenCategories.end()) {
198 toBeRemoved = false;
199 }
200 }
201
202 if (!toBeRemoved) {
203 // just mark them to be removed as we are iterating around the vector
204 // at the moment
205 validNames.emplace_back(name);
206 }
207 }
208 return validNames;
209 }
210}
211
216std::optional<std::pair<std::string, int>>
217AlgorithmFactoryImpl::getRealNameFromAlias(const std::string &alias) const noexcept {
218 auto a_it = m_amap.find(alias);
219 if (a_it == m_amap.end())
220 return std::nullopt;
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 if (const auto realNameAndVersion = getRealNameFromAlias(algorithmName)) {
238 return realNameAndVersion->second;
239 } else {
240 throw std::runtime_error("AlgorithmFactory::highestVersion() - Unknown algorithm '" + algorithmName + "'");
241 }
242 }
243}
244
252const std::map<std::string, bool> AlgorithmFactoryImpl::getCategoriesWithState() const {
253 std::map<std::string, bool> resultCategories;
254
255 // hidden categories - empty initially
256 std::unordered_set<std::string> hiddenCategories;
257 fillHiddenCategories(&hiddenCategories);
258
259 // get all of the algorithm keys, including the hidden ones for speed
260 // purposes we will filter later if required
261 std::vector<std::string> names = getKeys(true);
262
263 std::vector<std::string>::const_iterator itr_end = names.end();
264 // for each algorithm
265 for (std::vector<std::string>::const_iterator itr = names.begin(); itr != itr_end; ++itr) {
266 std::string name = *itr;
267 // decode the name and create an instance
268 std::pair<std::string, int> namePair = decodeName(name);
269 std::shared_ptr<IAlgorithm> alg = create(namePair.first, namePair.second);
270 // extract out the categories
271 std::vector<std::string> categories = alg->categories();
272
273 // for each category of the algorithm
274 std::vector<std::string>::const_iterator itCategoriesEnd = categories.end();
275 for (std::vector<std::string>::const_iterator itCategories = categories.begin(); itCategories != itCategoriesEnd;
276 ++itCategories) {
277 bool isHidden = true;
278 // check if the category is hidden
279 if (hiddenCategories.find(*itCategories) == hiddenCategories.end()) {
280 isHidden = false;
281 }
282 resultCategories[*itCategories] = isHidden;
283 }
284 }
285 return resultCategories;
286}
287
296const std::unordered_set<std::string> AlgorithmFactoryImpl::getCategories(bool includeHidden) const {
297 std::unordered_set<std::string> validCategories;
298
299 // get all of the information we need
300 auto categoryMap = getCategoriesWithState();
301
302 // iterate around the map
303 for (auto const &category : categoryMap) {
304 bool isHidden = (category).second;
305 if (includeHidden || (!isHidden)) {
306 validCategories.insert((category).first);
307 }
308 }
309
310 return validCategories;
311}
312
324std::vector<AlgorithmDescriptor> AlgorithmFactoryImpl::getDescriptors(bool includeHidden, bool includeAliases) const {
325 // algorithm names
326 auto sv = getKeys(true);
327
328 // hidden categories
329 std::unordered_set<std::string> hiddenCategories;
330 if (!includeHidden) {
331 fillHiddenCategories(&hiddenCategories);
332 }
333
334 // results vector
335 std::vector<AlgorithmDescriptor> res;
336
337 for (const auto &s : sv) {
338 if (s.empty())
339 continue;
341 size_t i = s.find('|');
342 if (i == std::string::npos) {
343 desc.name = s;
344 desc.version = 1;
345 } else if (i > 0) {
346 desc.name = s.substr(0, i);
347 std::string vers = s.substr(i + 1);
348 desc.version = vers.empty() ? 1 : std::stoi(vers);
349 } else
350 continue;
351
352 std::shared_ptr<IAlgorithm> alg = create(desc.name, desc.version);
353 auto categories = alg->categories();
354 desc.alias = alg->alias();
355
356 // For each category
357 auto itCategoriesEnd = categories.end();
358 for (auto itCategories = categories.begin(); itCategories != itCategoriesEnd; ++itCategories) {
359 desc.category = *itCategories;
360
361 // Let's check if this category or any of its parents are hidden.
362 bool categoryIsHidden = false;
363
364 // Split the category into its components.
365 std::vector<std::string> categoryLayers;
366 boost::split(categoryLayers, desc.category, boost::is_any_of("\\"));
367
368 // Traverse each parent category, working our way from the top down.
369 std::string currentLayer;
370 for (auto &categoryLayer : categoryLayers) {
371 currentLayer.append(categoryLayer);
372
373 if (hiddenCategories.find(currentLayer) != hiddenCategories.end()) {
374 // Category is hidden, no need to check any others.
375 categoryIsHidden = true;
376 break;
377 }
378
379 // Add a separator in case we're going down another layer.
380 currentLayer.append("\\");
381 }
382
383 if (!categoryIsHidden) {
384 res.emplace_back(desc);
385 // Add alias to results if included
386 if (!desc.alias.empty() && includeAliases) {
387 // Avoid adding lowercase aliases to the algorithm tab
388 std::string lowerCaseName = desc.name;
389 std::transform(desc.name.cbegin(), desc.name.cend(), lowerCaseName.begin(),
390 [](unsigned char c) { return std::tolower(c); });
391 if (lowerCaseName != desc.alias) {
392 res.emplace_back(AlgorithmDescriptor{desc.alias, desc.version, desc.category, ""});
393 }
394 }
395 }
396 }
397 }
398 return res;
399}
400
401void AlgorithmFactoryImpl::fillHiddenCategories(std::unordered_set<std::string> *categorySet) const {
402 std::string categoryString = Kernel::ConfigService::Instance().getString("algorithms.categories.hidden");
403 Mantid::Kernel::StringTokenizer tokenizer(categoryString, ";",
406 std::copy(tokenizer.begin(), tokenizer.end(), std::inserter(*categorySet, categorySet->end()));
407}
408
413const std::string AlgorithmFactoryImpl::extractAlgName(const std::shared_ptr<IAlgorithm> &alg) const {
414 return alg->name();
415}
416
421int AlgorithmFactoryImpl::extractAlgVersion(const std::shared_ptr<IAlgorithm> &alg) const { return alg->version(); }
422
427const std::string AlgorithmFactoryImpl::extractAlgAlias(const std::shared_ptr<IAlgorithm> &alg) const {
428 return alg->alias();
429}
430
439std::shared_ptr<Algorithm> AlgorithmFactoryImpl::createAlgorithm(const std::string &name, const int version) const {
441}
442
443} // namespace Mantid::API
std::string name
Definition Run.cpp:60
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.
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.
std::optional< std::pair< std::string, int > > getRealNameFromAlias(const std::string &alias) const noexcept
Get an algorithms name and version from the alias map.
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:76
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: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
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
Structure uniquely describing an algorithm with its name, category and version.
std::string name
Algorithm Name.