PropertyDefinition.hpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* ArmarX is free software; you can redistribute it and/or modify
5* it under the terms of the GNU Lesser General Public License as
6* published by the Free Software Foundation; either version 2 of
7* the License, or (at your option) any later version.
8*
9* ArmarX is distributed in the hope that it will be useful, but
10* WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12* GNU Lesser General Public License for more details.
13*
14* You should have received a copy of the GNU General Public License
15* along with this program. If not, see <http://www.gnu.org/licenses/>.
16*
17* @package ArmarX::
18* @author Mirko Waechter ( mirko.waechter at kit dot edu)
19* @date 2015
20* @copyright http://www.gnu.org/licenses/gpl.txt
21* GNU General Public License
22*/
23
24
25#pragma once
26
27#include <cstddef>
28#include <cstdlib>
29#include <exception>
30#include <functional>
31#include <iostream>
32#include <limits>
33#include <map>
34#include <ostream>
35#include <string>
36#include <type_traits>
37#include <typeinfo>
38#include <vector>
39
40#include <IceUtil/Time.h>
41
42#include <SimoxUtility/algorithm/get_map_keys_values.h>
43
55
56namespace armarx
57{
58 template <typename PropertyType>
60 PropertyType* setterRef,
61 const std::string& propertyName,
62 const std::string& description,
68 factory(nullptr),
69 regex(""),
70 caseInsensitive(true),
71 expandEnvVars(true),
73 max(std::numeric_limits<double>::max()),
74 min(-std::numeric_limits<double>::max())
75 {
76 initHook();
77 }
78
79 template <typename PropertyType>
81 PropertyType* setterRef,
82 const std::string& propertyName,
83 PropertyType defaultValue,
84 const std::string& description,
91 factory(nullptr),
92 regex(""),
93 caseInsensitive(true),
94 expandEnvVars(true),
96 max(std::numeric_limits<double>::max()),
97 min(-std::numeric_limits<double>::max())
98 {
99 initHook();
100 }
101
102 template <typename PropertyType>
104 std::function<void(const PropertyType&)> setter,
105 const std::string& propertyName,
106 PropertyType defaultValue,
107 const std::string& description,
113 setterRef{nullptr},
114 setter(setter),
115 factory(nullptr),
116 regex(""),
117 caseInsensitive(true),
118 expandEnvVars(true),
119 stringRemoveQuotes(true),
120 max(std::numeric_limits<double>::max()),
121 min(-std::numeric_limits<double>::max())
122 {
123 initHook();
124 }
125
126 std::string PropertyDefinition_toLowerCopy(std::string const& input);
127
128 template <typename PropertyType>
130 PropertyDefinition<PropertyType>::map(const std::string& valueString, PropertyType value)
131 {
132 if (caseInsensitive)
133 {
134 std::string lowerCaseValueString = PropertyDefinition_toLowerCopy(valueString);
135
136 propertyValuesMap[lowerCaseValueString] = ValueEntry(valueString, value);
137 }
138 else
139 {
140 propertyValuesMap[valueString] = ValueEntry(valueString, value);
141 }
142
143 return *this;
144 }
145
146 template <typename PropertyType>
148 PropertyDefinition<PropertyType>::map(const std::map<std::string, PropertyType>& values)
149 {
150 for (const auto& [name, value] : values)
151 {
152 map(name, value);
153 }
154 return *this;
155 }
156
157 template <typename PropertyType>
158 template <class T>
159 inline std::enable_if_t<std::is_same_v<T, PropertyType> && !std::is_same_v<T, std::string>,
161 PropertyDefinition<PropertyType>::map(const std::map<T, std::string>& values)
162 {
163 for (const auto& [value, name] : values)
164 {
165 map(name, value);
166 }
167 return *this;
168 }
169
170 template <typename PropertyType>
174 this->factory = func;
175
176 return *this;
177 }
178
179 template <typename PropertyType>
182 {
184 {
185 // rebuild value map
186 PropertyValuesMap newPropertyValuesMap;
187
188 typename PropertyValuesMap::iterator valueIter = propertyValuesMap.begin();
189
190 std::string key;
191
192 while (valueIter != propertyValuesMap.end())
193 {
194 key = valueIter->second.first;
195
197 {
199 }
200
201 newPropertyValuesMap[key] = valueIter->second;
202 valueIter++;
203 }
204
205 // set the new map
206 propertyValuesMap = newPropertyValuesMap;
208 }
209
210 return *this;
211 }
212
213 template <typename PropertyType>
214 PropertyDefinition<PropertyType>&
216 {
217 this->expandEnvVars = expand;
218 return *this;
219 }
220
221 template <typename PropertyType>
228
229 template <typename PropertyType>
232 {
233 this->regex = match_regex;
234
235 return *this;
236 }
237
238 template <typename PropertyType>
241 {
242 if constexpr (std::is_arithmetic_v<PropertyType>)
243 {
244 this->min = min_value;
245
246 return *this;
247 }
248 else
249 {
250 ARMARX_WARNING << "Cannot use setMin() to " << min_value
251 << " on a non-arithmetic type. Ignoring.";
252 return *this;
253 }
254 }
255
256 template <typename PropertyType>
259 {
260 if constexpr (std::is_arithmetic_v<PropertyType>)
261 {
262 this->max = max_value;
263
264 return *this;
265 }
266 else
267 {
268 ARMARX_WARNING << "Cannot use setMax() to " << max_value
269 << " on a non-arithmetic type. Ignoring.";
270 return *this;
274 template <typename PropertyType>
275 bool
280
281 template <typename PropertyType>
282 bool
285 return expandEnvVars;
286 }
288 template <typename PropertyType>
289 bool
291 {
293 }
294
295 template <typename PropertyType>
296 std::string
301
302 template <typename PropertyType>
303 double
305 {
306 return min;
307 }
308
309 template <typename PropertyType>
310 double
312 {
313 return max;
314 }
315
316 template <typename PropertyType>
317 std::string
322
323 template <typename PropertyType>
324 std::string
329
330 template <typename PropertyType>
336
337 template <typename PropertyType>
338 PropertyType
340 {
341 // throw exception if no default exists
342
343 if (isRequired())
344 {
345 throw armarx::LocalException("Required property '" + getPropertyName() +
346 "': default doesn't exist.");
347 }
348
349 return defaultValue;
350 }
351
352 template <typename PropertyType>
358
359 template <typename PropertyType>
360 std::string
362 const std::string& value)
363 {
364 std::vector<std::string> mapValues;
365
366 typename PropertyValuesMap::iterator it = propertyValuesMap.begin();
367
368 while (it != propertyValuesMap.end())
369 {
370 mapValues.push_back(it->second.first);
371
372 ++it;
373 }
374
375 return formatter.formatDefinition(propertyName,
377 (min != -std::numeric_limits<double>::max()
379 : ""),
380 (max != std::numeric_limits<double>::max()
382 : ""),
384 (!isCaseInsensitive() ? "yes" : "no"),
385 (isRequired() ? "yes" : "no"),
386 regex,
387 mapValues,
388 value);
389 }
390
391 template <typename PropertyType>
392 std::string
394 {
395 if (not isRequired())
396 {
398 {
400 }
401 else if constexpr (std::is_same_v<PropertyType, IceUtil::Time>)
402 {
403 IceUtil::Time time = getDefaultValue();
404 double time_count = time.toMicroSecondsDouble();
405 std::string unit = "µs";
406
407 if (time_count >= 1000)
408 {
409 time_count /= 1000;
410 unit = "ms";
411
412 if (time_count >= 1000)
413 {
414 time_count /= 1000;
415 unit = "s";
416
417 if (time_count >= 60)
418 {
419 time_count /= 60;
420 unit = "min";
421
422 if (time_count >= 60)
423 {
424 time_count /= 60;
425 unit = "h";
426
427 if (time_count >= 24)
428 {
429 return time.toDateTime();
430 }
431 }
432 }
433 }
434 }
435
436 return PropertyDefinition_lexicalCastToString(time_count) + " " + unit;
437 }
438 // bool
439 else if constexpr (std::is_same_v<PropertyType, bool>)
440 {
441 return getDefaultValue() ? "true" : "false";
442 }
443 // floating point types
444 else if constexpr (std::is_floating_point_v<PropertyType>)
445 {
447 }
448 // integral types
449 else if constexpr (std::is_integral_v<PropertyType>)
450 {
452 }
453 // std::string
454 else if constexpr (std::is_same_v<PropertyType, std::string>)
455 {
456 if (getDefaultValue().empty())
457 {
458 return "\"\"";
459 }
460
461 return getDefaultValue();
462 }
463 // mappings
464 else
465 {
466 typename PropertyValuesMap::iterator it = propertyValuesMap.begin();
467
468 while (it != propertyValuesMap.end())
469 {
470 if (it->second.second == getDefaultValue())
471 {
472 return it->second.first;
473 }
474
475 ++it;
476 }
477
478 return "Default value not mapped.";
479 }
480 }
481
482 // no default available
483 return std::string();
484 }
485
486 template <typename PropertyType>
487 PropertyType
489 Ice::PropertiesPtr iceProperties)
490 {
491 return isRequired() ? getValueRequired(prefix, iceProperties)
492 : getValueOptional(prefix, iceProperties);
493 }
494
495 template <typename PropertyType>
496 void
498 Ice::PropertiesPtr iceProperties)
499 {
500 if (setterRef != nullptr)
501 {
502 *setterRef = getValue(prefix, iceProperties);
503 }
504 else if (setter.has_value())
505 {
506 (*setter)(getValue(prefix, iceProperties));
507 }
508 }
509
510 std::map<std::string, std::string>
511 PropertyDefinition_parseMapStringString(std::string const& input);
512 std::vector<std::string> PropertyDefinition_parseVectorString(std::string const& input);
513 IceUtil::Time PropertyDefinition_parseIceUtilTime(std::string const& input);
514
515 template <typename PropertyType>
516 void
517 PropertyDefinition<PropertyType>::initHook()
518 {
519 // bool
520 if constexpr (std::is_same_v<PropertyType, bool>)
521 {
522 setCaseInsensitive(true);
523 map("true", true);
524 map("false", false);
525 map("yes", true);
526 map("no", false);
527 map("1", true);
528 map("0", false);
529 }
530 // armarx::MessageType
531 else if constexpr (std::is_same_v<PropertyType, armarx::MessageTypeT>)
532 {
533 setCaseInsensitive(true);
534 map("Debug", armarx::MessageTypeT::DEBUG);
535 map("Verbose", armarx::MessageTypeT::VERBOSE);
536 map("Info", armarx::MessageTypeT::INFO);
537 map("Important", armarx::MessageTypeT::IMPORTANT);
538 map("Warning", armarx::MessageTypeT::WARN);
539 map("Error", armarx::MessageTypeT::ERROR);
540 map("Fatal", armarx::MessageTypeT::FATAL);
541 map("Undefined", armarx::MessageTypeT::UNDEFINED);
542 }
543 // std::map<std::string, std::string>
544 else if constexpr (std::is_same_v<PropertyType, std::map<std::string, std::string>>)
545 {
547 }
548 // std::vector<std::string>
549 else if constexpr (std::is_same_v<PropertyType, std::vector<std::string>>)
550 {
552 }
553 // IceUtil::Time
554 else if constexpr (std::is_same_v<PropertyType, IceUtil::Time>)
555 {
556 setCaseInsensitive(true);
558 }
560 {
562 }
563 }
564
565 template <typename PropertyType>
566 PropertyType
567 PropertyDefinition<PropertyType>::getValueRequired(const std::string& prefix,
568 Ice::PropertiesPtr iceProperties)
569 {
570 using namespace armarx::exceptions;
571
572 checkRequirement(prefix, iceProperties);
573
574 const std::string keyValue = getPropertyValue(prefix, iceProperties);
575 const std::string value = getRawPropertyValue(prefix, iceProperties);
576
577 if (matchRegex(value))
578 {
579 // Try factory.
580 if (getFactory())
581 {
582 try
583 {
584 return getFactory()(value);
585 }
586 catch (const armarx::LocalException& e)
587 {
588 throw local::InvalidPropertyValueException(prefix + getPropertyName(), value)
589 << e.getReason();
590 }
591 catch (const std::exception& e)
592 {
593 throw local::InvalidPropertyValueException(prefix + getPropertyName(), value)
594 << "Unknown exception while constructing type out of value: " << e.what();
595 }
596 catch (...)
597 {
598 throw local::InvalidPropertyValueException(prefix + getPropertyName(), value)
599 << "Unknown exception while constructing type out of value.";
600 }
601 }
602
603 // Try mapped values.
604 const PropertyValuesMap& propertyValuesMap = getValueMap();
605
606 if (!propertyValuesMap.empty())
607 {
608 auto valueIter = propertyValuesMap.find(keyValue);
609 if (valueIter != propertyValuesMap.end())
610 {
611 return processMappedValue(valueIter->second.second);
612 }
613
614 // Mapping exist but value not found.
615 throw local::UnmappedValueException(prefix + getPropertyName(), value)
616 << "Mapped keys: " << simox::alg::get_keys(getValueMap());
617 }
618
619 // Try parsing raw value.
620 // bool
621 if constexpr (std::is_same_v<PropertyType, bool>)
622 {
624 }
625 // arithmetic types
626 else if constexpr (std::is_arithmetic_v<PropertyType>)
627 {
628 try
629 {
630 PropertyType numericValue =
632 checkLimits(prefix, numericValue);
633 return numericValue;
634 }
635 catch (const std::bad_cast&)
636 {
637 throw local::InvalidPropertyValueException(prefix + getPropertyName(), value);
638 }
639 }
640 // string
641 else if constexpr (std::is_same_v<PropertyType, std::string>)
642 {
643 return processMappedValue(value);
644 }
645 else
646 {
647 throw exceptions::local::UnmappedValueException(prefix + getPropertyName(), value)
648 << "Mapped keys: " << simox::alg::get_keys(getValueMap());
649 }
650 }
651
652 throw local::InvalidPropertyValueException(prefix + getPropertyName(),
653 getRawPropertyValue(prefix, iceProperties));
654 }
655
656 template <typename PropertyType>
657 PropertyType
658 PropertyDefinition<PropertyType>::getValueOptional(const std::string& prefix,
659 Ice::PropertiesPtr iceProperties)
660 {
661 using namespace armarx::exceptions;
662
663 if (!isSet(prefix, iceProperties))
664 {
665 return getDefaultValue();
666 }
667 else
668 {
669 return getValueRequired(prefix, iceProperties);
670 }
671 }
672
673 template <typename PropertyType>
674 std::string
675 PropertyDefinition<PropertyType>::getPropertyValue(const std::string& prefix,
676 Ice::PropertiesPtr iceProperties) const
677 {
678 std::string value = icePropertyGet(iceProperties, prefix + getPropertyName());
679
680 if (isCaseInsensitive())
681 {
683 }
684
685 return value;
686 }
687
688 template <typename PropertyType>
689 std::string
690 PropertyDefinition<PropertyType>::getRawPropertyValue(const std::string& prefix,
691 Ice::PropertiesPtr iceProperties) const
692 {
693 return icePropertyGet(iceProperties, prefix + getPropertyName());
694 }
695
696 bool PropertyDefinition_matchRegex(std::string const& regex, std::string const& value);
697
698 template <typename PropertyType>
699 bool
700 PropertyDefinition<PropertyType>::matchRegex(const std::string& value) const
701 {
702 if (!getMatchRegex().empty())
703 {
704 return PropertyDefinition_matchRegex(getMatchRegex(), value);
705 }
706
707 return true;
708 }
709
710 template <typename PropertyType>
711 void
712 PropertyDefinition<PropertyType>::checkRequirement(const std::string& prefix,
713 Ice::PropertiesPtr iceProperties)
714 {
715 using namespace armarx::exceptions;
716
717 if (isRequired())
718 {
719 if (!isSet(prefix, iceProperties))
720 {
721 throw local::MissingRequiredPropertyException(prefix + getPropertyName(),
722 iceProperties);
723 }
724 }
725 }
726
727 template <typename PropertyType>
728 bool
730 Ice::PropertiesPtr iceProperties) const
731 {
732 return PropertyDefinitionBase::isSet(prefix, getPropertyName(), iceProperties);
733 }
734
735 template <typename PropertyType>
736 template <typename T>
737 T
738 PropertyDefinition<PropertyType>::processMappedValue(const T& value)
739 {
740 return value;
741 }
742
743 template <typename PropertyType>
744 std::string
745 PropertyDefinition<PropertyType>::processMappedValue(const std::string& value)
746 {
747 std::string result = value;
748 if (expandEnvironmentVariables())
749 {
750 expandEnvironmentVariables(result, result);
751 }
752 if (removeQuotes())
753 {
754 removeQuotes(result, result);
755 }
756 return result;
757 }
758
759 template <typename PropertyType>
760 bool
762 std::string& output)
763 {
764 output = input;
765
766 if (input == "")
767 {
768 return false;
769 }
770
771 bool res = true;
772 bool goOn = false;
773
774 do
775 {
776 size_t foundStart = output.find("${");
777
778 if (foundStart != std::string::npos)
779 {
780 goOn = true;
781 size_t foundEnd = output.find("}", foundStart);
782
783 if (foundEnd != std::string::npos)
784 {
785 std::string envVar = output.substr(foundStart + 2, foundEnd - foundStart - 2);
786 std::string replacement;
787 char* envVarENV = getenv(envVar.c_str());
788
789 if (envVarENV)
790 {
791 replacement = envVarENV;
792 }
793 else
794 {
795 std::cout << "WARNING: Could not expand environment variable: " << envVar
796 << std::endl;
797 }
798
799 output.replace(foundStart, foundEnd - foundStart + 1, replacement);
800 }
801 else
802 {
803 std::cout << "ERROR: Found '${' but missing '}' in " << input << std::endl;
804 goOn = false;
805 res = false;
806 }
807 }
808 else
809 {
810 goOn = false;
811 }
812
813 } while (goOn);
814
815 return res;
816 }
817
818 template <typename PropertyType>
819 void
820 PropertyDefinition<PropertyType>::removeQuotes(const std::string& input, std::string& output)
821 {
822 output = input;
823
824 if (input.size() < 2)
825 {
826 return;
827 }
828
829 if ((input[0] == '"' && input[input.size() - 1] == '"') ||
830 (input[0] == '\'' && input[input.size() - 1] == '\''))
831 {
832 output = input.substr(1, input.size() - 2);
833 }
834 }
835
836 template <typename PropertyType>
837 void
838 PropertyDefinition<PropertyType>::checkLimits(const std::string& prefix,
839 PropertyType numericValue) const
840 {
841 using namespace armarx::exceptions;
842
845
846 if ((dirtyValue < getMin()) || (dirtyValue > getMax()))
847 {
849 prefix + getPropertyName(), getMin(), getMax(), dirtyValue);
850 }
851 }
852} // namespace armarx
bool isSet(std::string const &prefix, std::string const &propertyName, Ice::PropertiesPtr const &iceProperties) const
PropertyDefinitionBase(bool required=true, PropertyConstness constness=eConstant)
PropertyDefinitionFormatter is the base class for all formatters of PropertyDefinitions.
virtual std::string formatDefinition(std::string name, std::string description, std::string min, std::string max, std::string default_, std::string casesensitivity, std::string requirement, std::string reged, std::vector< std::string > values, std::string value)=0
PropertyDefinition defines a property that will be available within the PropertyUser.
virtual void writeValueToSetter(const std::string &prefix, Ice::PropertiesPtr) override
double max
Upper bound of numeric values (used for numeric value retrieval without mapping)
PropertyFactoryFunction getFactory() const
bool caseInsensitive
Case sensitivity indicator.
PropertyDefinition< PropertyType > & setExpandEnvironmentVariables(bool expand)
Sets whether for string values environment varbiale expanding should be considered.
bool expandEnvVars
Exand environments variables indicator (standard: true)
std::string description
Property description.
PropertyValuesMap & getValueMap()
PropertyDefinition< PropertyType > & setCaseInsensitive(bool isCaseInsensitive)
Sets whether the property value matching is case insensitive.
bool isCaseInsensitive() const
std::optional< std::function< void(const PropertyType &)> > setter
PropertyDefinition< PropertyType > & map(const std::string &valueString, PropertyType value)
Maps a string value onto a value of the specified template type.
PropertyDefinition< PropertyType > & setMin(double min)
Sets the min allowed numeric value.
std::pair< std::string, PropertyType > ValueEntry
PropertyDefinition< PropertyType > & setRemoveQuotes(bool removeQuotes)
Sets whether for string values leading and trailing quotes should be removed.
std::string toString(PropertyDefinitionFormatter &formatter, const std::string &value) override
std::function< PropertyType(std::string)> PropertyFactoryFunction
std::map< std::string, ValueEntry > PropertyValuesMap
std::string getDefaultAsString() override
PropertyDefinition< PropertyType > & setFactory(const PropertyFactoryFunction &func)
Sets the factory function that creates the specified template type from the actual string value.
PropertyFactoryFunction factory
Builder function.
PropertyValuesMap propertyValuesMap
PropertyDefinition< PropertyType > & setMax(double max)
Sets the max allowed numeric value.
double min
Lower bound of numeric values (used for numeric value retrieval without mapping)
PropertyType defaultValue
Fallback/Default property value.
std::string getPropertyName() const
bool stringRemoveQuotes
Remove leading and trailing quotes indicator (standard: true) First and last character of a string pr...
PropertyType getValue(const std::string &prefix, Ice::PropertiesPtr)
std::string propertyName
Property name.
PropertyDefinition(PropertyType *setterRef, const std::string &propertyName, const std::string &description, PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
Constructs a property definition of a required property.
PropertyType * setterRef
Reference to a variable to set.
bool isSet(const std::string &prefix, Ice::PropertiesPtr iceProperties) const
std::string regex
Regular expression to approve a required value pattern.
PropertyDefinition< PropertyType > & setMatchRegex(const std::string &expr)
Sets the regular expression which the value has to be matched with.
This exception is thrown if a value specified for a property is not found in the property mapping Pro...
This exception is thrown if a value is out of a specified or allowed range.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
::IceInternal::Handle<::Ice::Properties > PropertiesPtr
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::string PropertyDefinition_lexicalCastToString(float input)
IceUtil::Time PropertyDefinition_parseIceUtilTime(std::string const &input)
std::string PropertyDefinition_toLowerCopy(const std::string &input)
double PropertyDefinition_lexicalCastTo< double >(std::string const &input)
std::vector< std::string > PropertyDefinition_parseVectorString(std::string const &input)
T PropertyDefinition_lexicalCastTo(std::string const &input)
bool PropertyDefinition_lexicalCastTo< bool >(std::string const &input)
bool PropertyDefinition_matchRegex(std::string const &regex, std::string const &value)
std::map< std::string, std::string > PropertyDefinition_parseMapStringString(const std::string &input)
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855
bool empty(const std::string &s)
Definition cxxopts.hpp:234