xref: /aosp_15_r20/external/libconfig/contrib/chained/libconfig_chained.h (revision 2e9d491483b805f09ea864149eadd5680efcc72a)
1 /* ----------------------------------------------------------------------------
2    libconfig - A library for processing structured configuration files
3    libconfig chained - Extension for reading the configuration and defining
4                        the configuration specification at once.
5    Copyright (C) 2016 Richard Schubert
6 
7    This file is part of libconfig contributions.
8 
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public License
11    as published by the Free Software Foundation; either version 2.1 of
12    the License, or (at your option) any later version.
13 
14    This library is distributed in the hope that it will be useful, but
15    WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Library General Public
20    License along with this library; if not, see
21    <http://www.gnu.org/licenses/>.
22    ----------------------------------------------------------------------------
23 */
24 
25 #pragma once
26 #ifndef _CHAINED_LIBCONFIG_H_
27 #define _CHAINED_LIBCONFIG_H_
28 
29 #include <libconfig.h++>
30 #include <cassert>
31 #include <fstream>
32 #include <sstream>
33 #include <iostream>
34 
35 namespace libconfig
36 {
37 	class ChainedSetting
38 	{
39 		struct Variant
40 		{
41 		private:
42 			bool			isSet;
43 			Setting::Type	type;
44 
45 			bool			value_bool;
46 			int64_t			value_int;
47 			double			value_float;
48 			std::string		value_string;
49 
50 		public:
51 
VariantVariant52 			Variant()
53 				: isSet(false)
54 				, type(Setting::TypeNone)
55 			{
56 			}
VariantVariant57 			Variant(bool value)
58 			{
59 				value_bool = value;
60 				isSet = true;
61 				type = Setting::TypeBoolean;
62 			}
VariantVariant63 			Variant(int32_t value)
64 			{
65 				value_int = value;
66 				isSet = true;
67 				type = Setting::TypeInt;
68 			}
VariantVariant69 			Variant(int64_t value)
70 			{
71 				value_int = value;
72 				isSet = true;
73 				type = Setting::TypeInt64;
74 			}
VariantVariant75 			Variant(double value)
76 			{
77 				value_float = value;
78 				isSet = true;
79 				type = Setting::TypeFloat;
80 			}
VariantVariant81 			Variant(std::string& value)
82 			{
83 				value_string = value;
84 				isSet = true;
85 				type = Setting::TypeString;
86 			}
VariantVariant87 			Variant(const char* value)
88 			{
89 				value_string = value;
90 				isSet = true;
91 				type = Setting::TypeString;
92 			}
93 
94 			operator bool() const { return value_bool; }
95 			operator int() const { return (int)value_int; }
96 			operator unsigned int() const { return (unsigned int)value_int; }
97 			operator long() const { return (long)value_int; }
98 			operator unsigned long() const { return (unsigned long)value_int; }
99 			operator long long() const { return (long long)value_int; }
100 			operator unsigned long long() const { return (unsigned long long)value_int; }
101 			operator double() const { return value_float; }
102 			operator float() const { return (float)value_float; }
stringVariant103 			operator std::string() const { return value_string; }
104 
IsSetVariant105 			const bool IsSet() const
106 			{
107 				return isSet;
108 			}
109 
GetTypeVariant110 			const Setting::Type GetType() const
111 			{
112 				return type;
113 			}
114 		};
115 
116 	public:
117 
118 		// Starting point for method chained libconfig.
119 		// Pass a custom ostream to intercept any error messages (useful for Applications with UI).
120 		ChainedSetting(Setting& setting, std::ostream& err = std::cerr)
121 			: name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : ""))
122 			, index(setting.getIndex())
123 			, parent(NULL)
124 			, setting(&setting)
125 			, err(err)
126 			, isSettingMandatory(false)
127 			, anySettingIsMissing(false)
128 			, anyMandatorySettingIsMissing(false)
129 			, capturedSpecification(NULL)
130 			, capturedSetting(NULL)
131 		{
132 		}
133 
134 		// Starts capturing any configuration readings into the temporary config object.
captureExpectedSpecification(Config * temporaryConfigSpecification)135 		void captureExpectedSpecification(Config* temporaryConfigSpecification)
136 		{
137 			capturedSpecification = temporaryConfigSpecification;
138 			capturedSetting = &capturedSpecification->getRoot();
139 		}
140 
141 		// Returns the captured configuration specification,
142 		// premised captureExpectedSpecification() was called earlier.
143 		// The path parameter is needed to write the configuration
144 		// to disk before it can be read into a usable string.
getCapturedSpecification(const std::string & tempFilePath)145 		std::string getCapturedSpecification(const std::string& tempFilePath)
146 		{
147 			try
148 			{
149 				capturedSpecification->writeFile(tempFilePath.c_str());
150 			}
151 			catch (const FileIOException&)
152 			{
153 				err << "I/O error while writing temporary setting file: " << tempFilePath << std::endl;
154 				return "";
155 			}
156 
157 			std::ifstream t(tempFilePath);
158 			if (!t.is_open())
159 			{
160 				err << "I/O error while reading temporary setting file: " << tempFilePath << std::endl;
161 				return "";
162 			}
163 			std::stringstream buffer;
164 			buffer << t.rdbuf();
165 
166 			capturedSpecification = NULL;
167 
168 			return buffer.str();
169 		}
170 
171 		// Defines the default value for this setting if missing from config file.
172 		template<typename T>
defaultValue(T defaultValue)173 		ChainedSetting& defaultValue(T defaultValue)
174 		{
175 			defaultVal = defaultValue;
176 			return *this;
177 		}
178 
179 		// Defines the inclusive minimum value for this setting.
180 		// A lesser value set in a configuration file will be clamped to this limit.
181 		template<typename T>
min(T min)182 		ChainedSetting& min(T min)
183 		{
184 			minVal = min;
185 			return *this;
186 		}
187 
188 		// Defines the inclusive maximum value for this setting.
189 		// A greater value set in a configuration file will be clamped to this limit.
190 		template<typename T>
max(T max)191 		ChainedSetting& max(T max)
192 		{
193 			maxVal = max;
194 			return *this;
195 		}
196 
197 		// Defines this setting to be mandatory.
198 		// Any mandatory value missing in the configuration file will raise an error.
199 		// Use isAnyMandatorySettingMissing() to check for any violations.
isMandatory()200 		ChainedSetting& isMandatory()
201 		{
202 			isSettingMandatory = true;
203 			if (parent) parent->isMandatory();
204 			return *this;
205 		}
206 
207 		template<typename T>
T()208 		operator T()
209 		{
210 			auto requestedType = GetRequestedType<T>();
211 			CheckType(defaultVal, requestedType);
212 			CheckType(minVal, requestedType);
213 			CheckType(maxVal, requestedType);
214 
215 			CaptureSetting<T>(requestedType);
216 
217 			if (!setting)
218 			{
219 				if (isSettingMandatory)
220 				{
221 					AlertMandatorySettingMissing<T>();
222 				}
223 				PropagateAnySettingIsMissing();
224 
225 				return GetDefaultValue<T>();
226 			}
227 
228 			try
229 			{
230 				T value = *setting;
231 				if (minVal.IsSet())
232 				{
233 					T min = minVal;
234 					if (value < min)
235 					{
236 						err << "'" << setting->getPath() << "' setting is out of valid bounds (min: " << min << "). Value was: " << value << std::endl;
237 						value = min;
238 					}
239 				}
240 				if (maxVal.IsSet())
241 				{
242 					T max = maxVal;
243 					if (value > max)
244 					{
245 						err << "'" << setting->getPath() << "' setting is out of valid bounds (max: " << max << "). Value was: " << value << std::endl;
246 						value = max;
247 					}
248 				}
249 				return value;
250 			}
251 			catch (const SettingTypeException& tex)
252 			{
253 				err << "'" << tex.getPath() << "' setting is of wrong type." << std::endl;
254 			}
255 
256 			return GetDefaultValue<T>();
257 		}
258 
259 		ChainedSetting operator[](const char *name)
260 		{
261 			CaptureSetting<Setting>(Setting::TypeGroup);
262 
263 			if (!setting)
264 			{
265 				return ChainedSetting(name, this);
266 			}
267 
268 			if(setting->exists(name))
269 			{
270 				return ChainedSetting((*setting)[name], this);
271 			}
272 			else
273 			{
274 				return ChainedSetting(name, this);
275 			}
276 		}
277 
278 		inline ChainedSetting operator[](const std::string &name)
279 		{
280 			return(operator[](name.c_str()));
281 		}
282 
283 		ChainedSetting operator[](int index)
284 		{
285 			// This could also be an TypeArray but we cannot be sure here.
286 			// By using TypeList we ensure it will always work.
287 			CaptureSetting<Setting>(Setting::TypeList);
288 
289 			if (!setting)
290 			{
291 				return ChainedSetting(index, this);
292 			}
293 
294 			if (index >= 0 && index < setting->getLength())
295 			{
296 				return ChainedSetting((*setting)[index], this);
297 			}
298 			else
299 			{
300 				return ChainedSetting(index, this);
301 			}
302 		}
303 
getLength()304 		int getLength() const
305 		{
306 			return setting ? setting->getLength() : 0;
307 		}
308 
getType()309 		Setting::Type getType() const
310 		{
311 			return setting ? setting->getType() : Setting::TypeNone;
312 		}
313 
314 		// Indicates whether this setting is present in the read configuration file.
exists()315 		bool exists() const
316 		{
317 			return setting != NULL;
318 		}
319 
isAnyMandatorySettingMissing()320 		bool isAnyMandatorySettingMissing() const
321 		{
322 			return anyMandatorySettingIsMissing;
323 		}
324 
isAnySettingMissing()325 		bool isAnySettingMissing() const
326 		{
327 			return anySettingIsMissing;
328 		}
329 
clearAnySettingMissingFlag()330 		void clearAnySettingMissingFlag()
331 		{
332 			anySettingIsMissing = false;
333 		}
334 
335 	private:
336 
ChainedSetting(Setting & setting,ChainedSetting * parent)337 		ChainedSetting(Setting& setting, ChainedSetting* parent)
338 			: name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : ""))
339 			, index(setting.getIndex())
340 			, parent(parent)
341 			, setting(&setting)
342 			, err(parent->err)
343 			, isSettingMandatory(false)
344 			, anySettingIsMissing(false)
345 			, anyMandatorySettingIsMissing(false)
346 			, capturedSpecification(NULL)
347 			, capturedSetting(NULL)
348 		{
349 		}
350 
ChainedSetting(const std::string & name,ChainedSetting * parent)351 		ChainedSetting(const std::string& name, ChainedSetting* parent)
352 			: name(name)
353 			, index(-1)
354 			, parent(parent)
355 			, setting(NULL)
356 			, err(parent->err)
357 			, isSettingMandatory(false)
358 			, anySettingIsMissing(true)
359 			, anyMandatorySettingIsMissing(false)
360 			, capturedSpecification(NULL)
361 			, capturedSetting(NULL)
362 		{
363 		}
364 
ChainedSetting(int index,ChainedSetting * parent)365 		ChainedSetting(int index, ChainedSetting* parent)
366 			: name("")
367 			, index(index)
368 			, parent(parent)
369 			, setting(NULL)
370 			, err(parent->err)
371 			, isSettingMandatory(false)
372 			, anySettingIsMissing(true)
373 			, anyMandatorySettingIsMissing(false)
374 			, capturedSpecification(NULL)
375 			, capturedSetting(NULL)
376 		{
377 		}
378 
379 		template<typename T>
ConditionalSetCapturedDefaultValue()380 		void ConditionalSetCapturedDefaultValue()
381 		{
382 			*capturedSetting = GetDefaultValue<T>();
383 		}
384 
385 
386 
387 		template<typename T>
CaptureSetting(Setting::Type type)388 		void CaptureSetting(Setting::Type type)
389 		{
390 			if (!capturedSetting && parent && parent->capturedSetting)
391 			{
392 				if (name.length() > 0)
393 				{
394 					if (!parent->capturedSetting->exists(name))
395 					{
396 						capturedSetting = &parent->capturedSetting->add(name, type);
397 					}
398 					else
399 					{
400 						capturedSetting = &(*parent->capturedSetting)[name.c_str()];
401 					}
402 				}
403 				else
404 				{
405 					if (index < parent->capturedSetting->getLength())
406 					{
407 						capturedSetting = &(*parent->capturedSetting)[0];
408 					}
409 					else
410 					{
411 						assert(index == parent->capturedSetting->getLength()); // you requested an index while omitting at least one of its previous siblings
412 						capturedSetting = &parent->capturedSetting->add(type);
413 					}
414 				}
415 
416 				ConditionalSetCapturedDefaultValue<T>();
417 			}
418 		}
419 
420 
GetPath()421 		std::string GetPath() const
422 		{
423 			if (setting)
424 			{
425 				return setting->getPath();
426 			}
427 
428 			std::string path = (name.length() > 0) ? name : "[" + std::to_string(index) + "]";
429 			if (parent)
430 			{
431 				auto parentPath = parent->GetPath();
432 				return (parentPath.length() > 0) ? (parentPath + ((name.length() == 0) ? "" : ".") + path) : path;
433 			}
434 			return path;
435 		}
436 
PropagateAnySettingIsMissing()437 		void PropagateAnySettingIsMissing()
438 		{
439 			anySettingIsMissing = true;
440 			if (parent)
441 			{
442 				parent->PropagateAnySettingIsMissing();
443 			}
444 		}
445 
PropagateAnyMandatorySettingIsMissing()446 		void PropagateAnyMandatorySettingIsMissing()
447 		{
448 			anyMandatorySettingIsMissing = true;
449 			if (parent)
450 			{
451 				parent->PropagateAnyMandatorySettingIsMissing();
452 			}
453 		}
454 
455 		template<typename T>
AlertMandatorySettingMissing()456 		void AlertMandatorySettingMissing()
457 		{
458 			PropagateAnyMandatorySettingIsMissing();
459 
460 			err << "Missing '" << GetPath() << "' setting in configuration file." << std::endl;
461 		}
462 
463 		template<typename T>
GetUnsetDefaultValue()464 		T GetUnsetDefaultValue() const
465 		{
466 			return (T)0;
467 		}
468 
469 
470 
471 		template<typename T>
GetDefaultValue()472 		T GetDefaultValue() const
473 		{
474 			if (defaultVal.IsSet())
475 			{
476 				return (T)defaultVal;
477 			}
478 
479 			return GetUnsetDefaultValue<T>();
480 		}
481 
482 		template<typename T>
GetRequestedType()483 		Setting::Type GetRequestedType() const
484 		{
485 			// TODO @ Hemofektik: Check whether the outcommented line is still needed. static_assert(false) is checked on compile time and, well, asserts :)
486             // static_assert(false, "should never happen, unless you requested an unsupported type");
487 			return Setting::TypeNone;
488 		}
489 
490 
CheckType(const Variant & variant,Setting::Type expectedType)491 		void CheckType(const Variant& variant, Setting::Type expectedType) const
492 		{
493 			if (!variant.IsSet()) return;
494 			if(expectedType != variant.GetType())
495 			{
496 				assert(false); // fix your code to match the whole chain of this setting to one single type!
497 				err << "'" << GetPath() << "' setting limits or default value is of incompatible type." << std::endl;
498 			}
499 		}
500 
501 		std::string name;
502 		int index;
503 		ChainedSetting* parent;
504 		Setting* setting;
505 		std::ostream& err;
506 		Variant defaultVal;
507 		Variant minVal;
508 		Variant maxVal;
509 		bool isSettingMandatory;
510 		bool anySettingIsMissing;
511 		bool anyMandatorySettingIsMissing;
512 		Config* capturedSpecification;
513 		Setting* capturedSetting;
514 	};
515 
516     template<>
517     inline
518     void ChainedSetting::ConditionalSetCapturedDefaultValue<Setting>() { }
519 
520 	template<>
521     inline
GetUnsetDefaultValue()522     std::string ChainedSetting::GetUnsetDefaultValue() const
523     {
524         return "";
525     }
526 
527 
528     template<> inline Setting::Type ChainedSetting::GetRequestedType<int8_t>() const { return Setting::TypeInt; }
529     template<> inline Setting::Type ChainedSetting::GetRequestedType<uint8_t>() const { return Setting::TypeInt; }
530     template<> inline Setting::Type ChainedSetting::GetRequestedType<int16_t>() const { return Setting::TypeInt; }
531     template<> inline Setting::Type ChainedSetting::GetRequestedType<uint16_t>() const { return Setting::TypeInt; }
532     template<> inline Setting::Type ChainedSetting::GetRequestedType<int32_t>() const { return Setting::TypeInt; }
533     template<> inline Setting::Type ChainedSetting::GetRequestedType<uint32_t>() const { return Setting::TypeInt; }
534     template<> inline Setting::Type ChainedSetting::GetRequestedType<int64_t>() const { return Setting::TypeInt64; }
535     template<> inline Setting::Type ChainedSetting::GetRequestedType<uint64_t>() const { return Setting::TypeInt64; }
536     template<> inline Setting::Type ChainedSetting::GetRequestedType<float>() const { return Setting::TypeFloat; }
537     template<> inline Setting::Type ChainedSetting::GetRequestedType<double>() const { return Setting::TypeFloat; }
538     template<> inline Setting::Type ChainedSetting::GetRequestedType<std::string>() const { return Setting::TypeString; }
539     template<> inline Setting::Type ChainedSetting::GetRequestedType<bool>() const { return Setting::TypeBoolean; }
540 }
541 
542 #endif