ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
configuration.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !defined(ALIB_DOX)
10# if !defined (HPP_ALIB_CONFIG_CONFIGURATION)
12# endif
13# if !defined(HPP_ALIB_CONFIG_CONFIG)
14# include "alib/config/config.hpp"
15# endif
16
17# if !defined(HPP_ALIB_CAMP_MESSAGE_REPORT)
19# endif
20
21# if !defined(HPP_ALIB_MONOMEM_HASHSET)
23# endif
24
25# if !defined (HPP_ALIB_LANG_CAMP_INLINES)
27# endif
28#endif // !defined(ALIB_DOX)
29
30namespace alib {
31
32
33/** ************************************************************************************************
34 * This is the namespace of\alibmod, which implements access, evaluation and writing of
35 * configuration variables. Variable data can be load from different sources, for example
36 * configuration files, command line parameters, environment variables, or any custom storage type.
37 *
38 * Please consult \ref alib_mod_config "ALib Module Config - Programmer's Manual"
39 * for further information.
40 **************************************************************************************************/
41namespace config {
42
43
44
45// #################################################################################################
46// Configuration
47// #################################################################################################
48
50{
51 TrueValues= { A_CHAR("1"), A_CHAR("true"), A_CHAR("t"), A_CHAR("yes"), A_CHAR("y"), A_CHAR("on"), A_CHAR("ok") };
52
53 if( addPlugins == lang::CreateDefaults::Yes )
54 {
55 InsertPlugin( new InMemoryPlugin(CONFIG.GetResource("CfgPlgDef") ), Priorities::DefaultValues , lang::Responsibility::Transfer);
56 InsertPlugin( new Environment ( ), Priorities::Environment , lang::Responsibility::Transfer);
57 InsertPlugin( new CLIArgs ( ), Priorities::CLI , lang::Responsibility::Transfer);
58 InsertPlugin( new InMemoryPlugin(CONFIG.GetResource("CfgPlgPro") ), Priorities::ProtectedValues , lang::Responsibility::Transfer);
59 }
60}
61
62
64{
65 InMemoryPlugin* defaultPlugin= GetPluginTypeSafe<InMemoryPlugin>( Priorities::DefaultValues );
66 ALIB_ASSERT_ERROR( defaultPlugin, "CONFIG",
67 "Utility method FetchFromDefault used without default plugin in place." )
68
69 int cntCopied= 0;
70 Variable variable;
71 for( auto& section : defaultPlugin->sections )
72 {
73 if( sectionName.IsNotEmpty() && !sectionName.Equals( section.Name() ) )
74 continue;
75
76 for( auto entryIt= section.entries.begin() ; entryIt != section.entries.end() ; ++entryIt )
77 {
78 if( !dest.Load( variable.Declare( section.name, entryIt->Name() ), true ) )
79 {
80 defaultPlugin->Load ( variable );
81 dest.Store( variable );
82 ++cntCopied;
83 }
84 }
85 }
86
87 return cntCopied;
88}
89
90bool Configuration::IsTrue( const String& value )
91{
92 for ( auto& it : TrueValues )
93 if ( value.Equals<false, lang::Case::Ignore>( it ) )
94 return true;
95
96 return false;
97}
98
99
100
101// #############################################################################################
102// Load/Store
103// #############################################################################################
105{
106 variable.SetConfig (this);
107 variable.SetPriority( loadImpl( variable, true ) );
108
109 if ( variable.Priority() == Priorities::NONE
110 && variable.DefaultValue().IsNotNull() )
111 {
112 Store( variable, variable.DefaultValue() );
113
114 // if this now did not set the values although we have a default value, then
115 // no plug-in seemed to be able to store the variable. Nevertheless, we need to
116 // set the default value, as loading failed.
117 if( variable.Size() == 0 && variable.DefaultValue() != NullString() )
118 XTernalizer().LoadFromString( variable, variable.DefaultValue() );
119 }
120 return variable.Priority();
121}
122
123Priorities Configuration::Store( Variable& variable, const String& externalizedValue )
124{
125 // checks
126 if( externalizedValue.IsNull() )
127 {
128 if ( variable.Name().IsEmpty())
129 {
130 ALIB_ERROR( "CONFIG", "Trying to store an undefined variable." )
131 return Priorities::NONE;
132 }
133
134 if ( variable.Size() > 1 && variable.Delim() == '\0' )
135 {
136 ALIB_ERROR( "CONFIG", "Trying to store variable {!Q} which has multiple values "
137 "set but no delimiter defined.", variable.Fullname() )
138 return Priorities::NONE;
139 }
140 }
141
142 // set us as config
143 variable.SetConfig( this );
144
145 // detect?
146 if ( variable.Priority() <= Priorities::NONE )
147 {
148 variable.SetPriority( Priorities::NONE );
149 for ( auto& ppp : plugins )
150 if ( ppp.plugin->Load( variable, true ) )
151 {
152 variable.SetPriority( ppp.priority );
153 break;
154 }
155
156 if( variable.Priority() == Priorities::ProtectedValues )
157 {
158 variable.SetPriority( Priorities::NONE);
159 return Priorities::NONE;
160 }
161 }
162
163 // new variables go to default
164 if ( variable.Priority() == Priorities::NONE )
165 variable.SetPriority( Priorities::DefaultValues);
166
167 // store
168 for ( auto& ppp : plugins )
169 if ( ppp.priority <= variable.Priority()
170 && ppp.plugin->Store( variable, externalizedValue ) )
171 {
172 variable.SetPriority(ppp.priority);
173 return ppp.priority;
174 }
175
176 variable.SetPriority( Priorities::NONE );
177 return Priorities::NONE;
178}
179
180
181// #################################################################################################
182 // convenience methods using Configuration::Default singleton
183 // #################################################################################################
184Priorities Configuration::StoreDefault( Variable& variable, const String& externalizedValue )
185{
186 ConfigurationPlugin* defaultPlugin= GetPlugin( Priorities::DefaultValues );
187 ALIB_ASSERT_ERROR( defaultPlugin, "CONFIG",
188 "Utility method StoreDefault used without default plugin in place." )
189
190 if ( externalizedValue.IsNotNull() )
191 defaultPlugin->StringConverter->LoadFromString( variable, externalizedValue );
192
193 if ( variable.Size() == 0 && variable.DefaultValue().IsNotNull() )
194 defaultPlugin->StringConverter->LoadFromString( variable, variable.DefaultValue() );
195
196 variable.SetPriority( Priorities::DefaultValues );
197 return Store( variable, NullString() );
198}
199
200Priorities Configuration::Protect( Variable& variable, const String& externalizedValue )
201{
202 ConfigurationPlugin* protectedPlugin= GetPlugin( Priorities::ProtectedValues );
203
204 if ( externalizedValue.IsNotNull() )
205 protectedPlugin->StringConverter->LoadFromString( variable, externalizedValue );
206
207 if ( variable.Size() == 0 && variable.DefaultValue().IsNotNull() )
208 protectedPlugin->StringConverter->LoadFromString( variable, variable.DefaultValue() );
209
210 ALIB_ASSERT_ERROR( protectedPlugin, "CONFIG",
211 "Utility method Protect used without default plugin in place." )
212
213 variable.SetPriority( Priorities::ProtectedValues );
214 return Store( variable, NullString() );
215}
216
217integer Configuration::LoadFromString( Variable& variable, const String& externalizedValue )
218{
219 ConfigurationPlugin* defaultPlugin= GetPlugin( Priorities::DefaultValues );
220 ALIB_ASSERT_ERROR( defaultPlugin, "CONFIG",
221 "Utility method LoadFromString used without default plugin in place." )
222 defaultPlugin->StringConverter->LoadFromString( variable, externalizedValue );
223 return variable.Size();
224}
225
226
227// #############################################################################################
228// internals
229// #############################################################################################
230Priorities Configuration::loadImpl( Variable& variable, bool substitute )
231{
232 variable.ClearValues();
233 if ( variable.Name().IsEmpty() )
234 {
235 ALIB_WARNING( "CONFIG", "Empty variable name given" )
236 return Priorities::NONE;
237 }
238
239 // search variable
240 Priorities priority= Priorities::NONE;
241 for ( auto& ppp : plugins )
242 if ( ppp.plugin->Load( variable ) )
243 {
244 priority= ppp.priority;
245 break;
246 }
247
248 // no substitution or not found?
249 if ( !substitute || priority == Priorities::NONE )
250 return priority;
251
252 // substitution in all values of variable
253 String512 valBuffer; valBuffer.DbgDisableBufferReplacementWarning();
254 for ( int valueNo= 0; valueNo < variable.Size(); ++valueNo )
255 {
256 integer searchStartIdx = 0;
257 int maxReplacements = 50;
258 do
259 {
260 const String& value= variable.GetString( valueNo );
261
262 // search start
263 integer repStart= value.IndexOf( SubstitutionVariableStart, searchStartIdx );
264 if ( repStart < 0 )
265 break;
266 searchStartIdx= repStart;
267 integer varStart= repStart + SubstitutionVariableStart.Length();
268
269 integer repLen;
270 integer varLen;
271
272 // search end in two different ways depending on setting of public field "SubstitutionVariableEnd"
274 {
276 if ( idx < 0 )
277 idx= value.Length();
278
279 varLen= idx - varStart;
280 repLen= idx - repStart;
281 }
282 else
283 {
284 integer idx= value.IndexOf ( SubstitutionVariableEnd, varStart );
285 if (idx < 0 )
286 {
287 ALIB_WARNING( "CONFIG", "End of substitution variable not found (while start was found). "
288 "Variable name: {} Value: {!Q}.",
289 variable.Fullname(), variable.GetString() )
290 break;
291 }
292
293 varLen= idx - varStart;
294 repLen= idx + SubstitutionVariableEnd.Length() - repStart;
295 }
296
297 // get variable name string
298 Substring replVarCategory(nullptr);
299 Substring replVarName= value.Substring( varStart, varLen );
300 if ( replVarName.IsEmpty() )
301 {
302 searchStartIdx+= SubstitutionVariableStart.Length()
304 continue;
305 }
306
307 // parse category from name
308 integer catSeparatorIdx= replVarName.IndexOf( '_' );
309 if (catSeparatorIdx >= 0 )
310 {
311 replVarCategory= replVarName.Substring<false>( 0, catSeparatorIdx );
312 replVarName = replVarName.Substring ( catSeparatorIdx + 1);
313 }
314
315 // load replacement variable
316 if ( replVarName.IsNotEmpty() )
317 {
318 tempVar.Declare( replVarCategory, replVarName, variable.Delim() );
319 loadImpl( tempVar, false );
320 }
321 else
322 tempVar.Reset();
323
324 // do the replacement (even if no variable was found)
325 if ( tempVar.Size() > 1 )
326 {
327 variable.ReplaceValue( valueNo, tempVar );
328
329 // repeat replacements in current value, as current value changed
330 --valueNo;
331 break;
332 }
333 else
334 {
335 valBuffer.Reset(value);
336 if ( tempVar.Size() == 1 )
337 valBuffer.ReplaceSubstring<false>( tempVar.GetString(), repStart, repLen );
338 else
339 valBuffer.Delete<false>( repStart, repLen );
340 variable.ReplaceValue( valueNo, valBuffer );
341 }
342
343 }
344 while( --maxReplacements );
345
346 // warn if max replacements
347 if( maxReplacements <= 0 )
348 {
349 ALIB_WARNING( "CONFIG", "Too many substitutions in variable {!Q}. "
350 "Probably a recursive variable definition?", variable.Fullname() )
351 }
352 }
353 return priority;
354}
355
356
357// #################################################################################################
358// Iterator
359// #################################################################################################
360//! @cond NO_DOX
361namespace {
362
363class IteratorImpl : public Configuration::Iterator
364{
365 Configuration& config;
366 String sectionName;
367
368 MonoAllocator allocator;
369 HashSet<String> variablesFound;
370
372
373 integer nextPlugin;
374
375 public:
376
377 IteratorImpl( Configuration& configuration, const String& pSectionName )
378 : config (configuration)
379 , sectionName (pSectionName)
380 , allocator (4096)
381 , variablesFound(&allocator)
382 , pluginIt (nullptr)
383 , nextPlugin (0)
384 {}
385
386 virtual ~IteratorImpl() override
387 {
388 if( pluginIt )
389 delete pluginIt;
390 }
391
392 void ResetToSection( const String& pSectionName ) override
393 {
394 sectionName= pSectionName;
395 variablesFound.Reset();
396 allocator.Reset();
397 if( pluginIt )
398 {
399 delete pluginIt;
400 pluginIt= nullptr;
401 }
402 nextPlugin= 0;
403
404 }
405
406
407 virtual bool Next() override
408 {
409 for(;;)
410 {
411 // next plugin?
412 while( pluginIt == nullptr )
413 {
414 // end of iteration?
415 if( nextPlugin >= config.CountPlugins() )
416 return false;
417
418 // It might be that a plug-in is not iterable and returns nullptr.
419 // Therefore the while loop
420 pluginIt= config.GetPlugin( nextPlugin++ )->GetIterator( sectionName );
421 }
422
423 // continue in case of end of plugin vars
424 if( !pluginIt->Next(Actual) )
425 {
426 delete pluginIt;
427 pluginIt= nullptr;
428 continue;
429 }
430
431 // check if variable already existed
432 if( variablesFound.InsertIfNotExistent( Actual.Name() ).second == false )
433 continue;
434
435 // set the config field and return success!
436 Actual.SetConfig( &config );
437 return true;
438 }
439 }
440};
441
442} // anonymous namespace
443
444Configuration::Iterator* Configuration::GetIterator( const String& sectionName )
445{
446 return new IteratorImpl( *this, sectionName );
447}
448//! @endcond
449
450}} // namespace [alib::config]
virtual bool Next(Variable &variable)=0
virtual bool Store(Variable &variable)
virtual ALIB_API bool Load(Variable &variable, bool searchOnly=false)=0
ALIB_API Configuration(lang::CreateDefaults addPlugins)
ALIB_API integer LoadFromString(Variable &variable, const String &externalizedValue)
ALIB_API Priorities Load(Variable &variable)
std::vector< String > TrueValues
ALIB_API Priorities Store(Variable &variable, const String &externalizedValue=nullptr)
ALIB_API bool IsTrue(const String &value)
ALIB_API Iterator * GetIterator(const String &sectionName)
ALIB_API Priorities Protect(Variable &variable, const String &externalizedValue=nullptr)
ALIB_API Priorities StoreDefault(Variable &variable, const String &externalizedValue=nullptr)
ALIB_API int FetchFromDefault(ConfigurationPlugin &dest, const String &section=NullString())
ALIB_API Priorities loadImpl(Variable &variable, bool substitute)
virtual ALIB_API bool Load(Variable &variable, bool searchOnly=false) override
const String & DefaultValue() const
Definition variable.hpp:597
integer Size() const
Definition variable.hpp:708
ALIB_API const String & Fullname()
Definition variable.cpp:266
void SetPriority(Priorities priority)
Definition variable.hpp:628
ALIB_API void ReplaceValue(int idx, const String &replacement)
Definition variable.cpp:188
character Delim() const
Definition variable.hpp:506
ALIB_API Variable & Declare(const VariableDecl &declaration, const Box &replacements)
Definition variable.cpp:77
const String & Name() const
Definition variable.hpp:496
const String & GetString(int idx=0)
Definition variable.hpp:780
Variable & ClearValues(int startIdx=0)
Definition variable.hpp:721
Priorities Priority() const
Definition variable.hpp:617
ALIB_API Variable & Reset(lang::CurrentData nameAndCategory=lang::CurrentData::Clear)
Definition variable.cpp:25
void SetConfig(Configuration *config)
Definition variable.hpp:477
virtual ALIB_API void LoadFromString(Variable &variable, const String &src)
Definition plugins.cpp:112
const String & GetResource(const NString &name)
void InsertPlugin(TPlugin *plugin, TPriorities priority, lang::Responsibility responsibility=lang::Responsibility::KeepWithSender)
InsertIfNotExistent(const KeyType &key, const MappedType &mapped)
ALIB_API void Reset(const Snapshot &snapshot=Snapshot())
TAString & Delete(integer regionStart, integer regionLength=MAX_LEN)
Definition astring.hpp:1490
TAString & ReplaceSubstring(const TString< TChar > &src, integer regionStart, integer regionLength)
Definition astring.hpp:1708
void DbgDisableBufferReplacementWarning()
Definition astring.hpp:353
constexpr bool IsNull() const
Definition string.hpp:395
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:889
constexpr bool IsEmpty() const
Definition string.hpp:414
constexpr bool IsNotEmpty() const
Definition string.hpp:420
constexpr integer Length() const
Definition string.hpp:357
constexpr bool IsNotNull() const
Definition string.hpp:402
integer IndexOfAny(const TString &needles, integer startIdx=0) const
Definition string.hpp:1083
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.hpp:314
bool Equals(const TString< TChar > &rhs) const
Definition string.hpp:573
#define ALIB_WARNING(...)
Definition alib.hpp:981
#define A_CHAR(STR)
#define ALIB_ERROR(...)
Definition alib.hpp:980
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
@ Include
Chooses inclusion.
@ Yes
Create default values.
@ Transfer
Transfers responsibility to the receiving party.
Definition alib.cpp:57
constexpr String NullString()
Definition string.hpp:2498
strings::TString< character > String
Type alias in namespace alib.
config::Config CONFIG
Definition config.cpp:40
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286
config::InMemoryPlugin InMemoryPlugin
Type alias in namespace alib.