ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
configuration.cpp
1
5
6
7namespace alib {
8
9//==================================================================================================
10/// This is the namespace of\alibmod, which implements access, evaluation and writing of
11/// configuration variables. Variable data can be load from different sources, for example
12/// configuration files, command-line parameters, environment variables, or any custom storage type.
13///
14/// Please consult #"alib_mod_variables;ALib Module Config - Programmer's Manual"
15/// for further information.
16//==================================================================================================
17namespace variables {
18
19
20//==================================================================================================
21// Implementation of class detail::NodeMaintainer
22//==================================================================================================
23namespace detail
24{
25void ConfigNodeHandler::FreeNode( typename TTree::Node& node, TTree& tree ) {
26 // delete node name
27 auto& cfg= static_cast<Configuration&>(tree);
28 cfg.Pool.free( const_cast<TTree::CharacterType*>(node.name.storage.Buffer()),
29 size_t(node.name.storage.Length()) * sizeof(TTree::CharacterType) );
30
31 // delete vdata
32 Entry& entry= node.data;
33 if( entry.data ) {
34 entry.meta->destruct(entry.data, cfg.Pool);
35 cfg.Pool().Free(entry.data, entry.meta->size() );
36} }
37} // namespace detail
38
39//==================================================================================================
40// Implementation of class Configuration
41//==================================================================================================
43: StringTree ( allocator, '/')
44, Pool ( allocator )
45, types ( allocator )
46, replacementDeclarations( allocator )
47, listeners ( allocator )
48, BooleanTokens ( allocator ) {
49 DbgSetDCSName("Configuration");
50
51 // Register built-in types
59
60 // read boolean false/true values from resources
61 if( createDefaults == lang::CreateDefaults::Yes) {
62 std::array<Token, 10> tokenBuf;
63 #if ALIB_CAMP
64 strings::util::LoadResourcedTokens( BASECAMP, "CFGBTF", tokenBuf.data() ALIB_DBG(, 10) );
65 for (size_t i = 0; i < tokenBuf.size(); i+= 2 )
66 BooleanTokens.emplace_back(tokenBuf[i], tokenBuf[i+1]);
67 #else
68 BooleanTokens.emplace_back(Token("False;I;1"), Token("True;I;1"));
69 BooleanTokens.emplace_back(Token( "0;I;1"), Token( "1;I;1"));
70 BooleanTokens.emplace_back(Token( "No;I;1"), Token( "Yes;I;1"));
71 BooleanTokens.emplace_back(Token( "Off;I;3"), Token( "On;I;2"));
72 BooleanTokens.emplace_back(Token( "---;I;1"), Token( "OK;I;2" ));
73 #endif
74
75 // create default plugins
76 auto& ma= GetAllocator();
77 InsertPlugin( environmentPlugin= ma().New<EnvironmentVariablesPlugin>(ma) );
78 InsertPlugin( cliPlugin= ma().New< CLIVariablesPlugin>(ma) );
79} }
80
82 // we have to delete all nodes before the invocation of the base destructor, because
83 // this would use our pool allocator on existing nodes (which is then destructed already).
85
86 ALIB_ASSERT_WARNING( listeners.empty(), "VARIABLES",
87 "{} remaining registered listeners when destruction configuration.", listeners.size() )
88}
89
91 lang::ContainerOp insertOrRemove,
92 int event,
93 const Variable* variable,
94 const StringTree::Cursor* subTree,
95 const String& variableName,
96 const String& pathPrefixGiven,
97 const String& pathSubstring ) {
98 // checks
99 ALIB_ASSERT_ERROR( variable==nullptr || variable->IsDeclared() , "VARIABLES", "Given variable not declared.")
100 ALIB_ASSERT_ERROR( variable==nullptr || &variable->AsCursor().Tree() == this , "VARIABLES", "Given variable does not belong to this configuration.")
101 ALIB_ASSERT_ERROR( subTree ==nullptr || subTree->IsValid() , "VARIABLES", "Invalid cursor given." )
102 ALIB_ASSERT_ERROR( subTree ==nullptr || &subTree ->Tree() == this , "VARIABLES", "Given cursor does not belong to this configuration.")
103
104 // remove / from path
105 const String pathPrefix= pathPrefixGiven.CharAtStart() == StringTree::Separator()
106 ? pathPrefixGiven.Substring(1)
107 : pathPrefixGiven;
108
109 //------------------------------------------ registration ----------------------------------------
110 if( insertOrRemove == lang::ContainerOp::Insert) {
111 listeners.emplace_back( ListenerRecord{ listener,
112 event,
113 variable ? variable->AsCursor().Export() : ConstCursorHandle(),
114 subTree ? subTree-> Export() : ConstCursorHandle(),
117 AStringPA(Pool) } );
118 listeners.back().variableName << variableName;
119 listeners.back().pathPrefix << pathPrefix;
120 listeners.back().pathSubstring<< pathSubstring;
121
122 return;
123 }
124
125 //----------------------------------------------- de ---------------------------------------------
126 for (auto it= listeners.begin() ; it != listeners.end() ; ++it )
127 if( it->listener == listener
128 && it->event == event
129 && it->variable == ( variable ? variable->AsCursor().Export() : ConstCursorHandle() )
130 && it->subTree == ( subTree ? subTree ->Export() : ConstCursorHandle() )
131 && it->variableName .Equals( variableName )
132 && it->pathPrefix .Equals( pathPrefix )
133 && it->pathSubstring.Equals( pathSubstring ) )
134 {
135 (void) listeners.erase( it );
136 return;
137 }
138
139 ALIB_WARNING( "VARIABLES", "Listener with matching set of parameters not found with "
140 "deregistration." )
141
142} // Configuration::registerListener
143
144
146 // checks
147 ALIB_ASSERT_ERROR( listener!=nullptr, "VARIABLES", "Given listener is nullptr.")
148
149 //----------------------------------------------- de ---------------------------------------------
150 int cnt= 0;
151 for (auto it= listeners.begin() ; it != listeners.end() ; )
152 if( it->listener == listener ) {
153 it= listeners.erase( it );
154 ++cnt;
155 }
156 else
157 ++it;
158
159 return cnt;
160} // Configuration::registerListener
161
163 const Variable& variable,
164 const String& variablePathGiven,
165 Priority previousPriority ) {
166 String256 variablePathBuffer;
167 const String* variablePath= &variablePathGiven;
168 for (auto it= listeners.begin() ; it != listeners.end() ; ++it )
169 if( event == it->event ) {
170 // if needed generate variable path
171 if( variablePath->IsEmpty()
172 && ( it->variableName .IsNotEmpty()
173 || it->pathPrefix .IsNotEmpty()
174 || it->pathSubstring.IsNotEmpty() ) )
175 {
176 variablePathBuffer << variable;
177 variablePath= &variablePathBuffer;
178 }
179
180 if( ( it->variable .IsValid() && ( it->variable == variable.AsCursor().Export() ) )
181 || ( it->subTree .IsValid() && ( variable.AsCursor().Distance( ImportCursor(it->subTree) ) >= 0 ) )
182 || ( it->variableName .IsNotEmpty() && it->variableName.Equals(variable.AsCursor().Name()) )
183 || ( it->pathPrefix .IsNotEmpty() && variablePath->StartsWith(it->pathPrefix) )
184 || ( it->pathSubstring.IsNotEmpty() && variablePath->IndexOf(it->pathSubstring) >= 0 )
185 )
186 {
187 it->listener->Notify( variable,
189 previousPriority );
190 } }
191} // Configuration::notifyListeners
192
193
194void Configuration::presetImportString( const String& name, const String& value,
195 const StringEscaper* escaper, Priority priority ) {
196 auto cursor= Root();
197 cursor.GoToCreateChildIfNotExistent(A_CHAR("$PRESETS"));
198
199 // nullptr given? Delete a preset.
200 if( value.IsNull() ) {
201 if( cursor.GoTo(name).IsNotEmpty() )
202 return; // nothing was previously set
203
204 auto& entry= *cursor;
205 if( entry.priority > priority )
206 return; // do not delete if lower priority!
207
208 if( entry.data ) {
209 entry.meta->destruct( entry.data, Pool );
210 Pool().Free( entry.data, entry.meta->size() );
211 entry.meta= nullptr;
212 entry.data= nullptr;
213 entry.priority= Priority::NONE;
214 }
215
216 // delete this node, but only if it is a leaf.
217 if( !cursor.HasChildren() )
218 cursor.Delete();
219
220 return;
221 }
222
223
224 // set-mode
225 cursor.GoToCreatedPathIfNotExistent(name);
226
227 // create a fake variable. We do not do this using the variable class, because this would
228 // raise assertions.
229 auto& entry= *cursor;
230 if( entry.data == nullptr ) {
231 auto it= types.Find(A_CHAR("S"));
232 ALIB_ASSERT_ERROR( it != types.end(), "VARIABLES",
233 "Variable type 'S' not registered. This usually cannot happen." )
234 auto* meta= entry.meta= *it;
235
236 // initialize fields
237 entry.data = static_cast<detail::VDATA*>(Pool().Alloc( meta->size(), alignof(detail::VDATA)));
238 meta->construct( cursor->data, Pool );
239 entry.priority= priority;
240 }
241
242 if( entry.priority <= priority ) {
243 entry.priority = priority;
244 entry.declaration= reinterpret_cast<const Declaration*>( escaper );
245 (Variable(cursor))= value;
246} }
247
248//##################################################################################################
249// Declaration replacement allocation
250//##################################################################################################
252 const Box& replacements ) {
253 //-------------------------------------- prepare replacement -------------------------------------
254 String128 replace;
255
256 const Box* replacementPtr;
257 integer qtyReplacements;
258 if ( replacements.IsArrayOf<Box>() ) {
259 replacementPtr = replacements.UnboxArray<Box>();
260 qtyReplacements= replacements.UnboxLength();
261 }
262 else if ( replacements.IsType<Boxes*>() ) {
263 const auto* boxes= replacements.Unbox<Boxes*>();
264 replacementPtr = boxes->data();
265 qtyReplacements= boxes->Size();
266 }
267 else if ( replacements.IsType<BoxesMA*>() ) {
268 const auto* boxes= replacements.Unbox<BoxesMA*>();
269 replacementPtr = boxes->data();
270 qtyReplacements= boxes->Size();
271 }
272 else if ( replacements.IsType<BoxesPA*>() ) {
273 const auto* boxes= replacements.Unbox<BoxesPA*>();
274 replacementPtr = boxes->data();
275 qtyReplacements= boxes->Size();
276 } else {
277 replacementPtr = &replacements;
278 qtyReplacements= 1;
279 }
280
281 //------------------------------------------ replace name ----------------------------------------
282 String256 bufName; ALIB_DBG( bufName .DbgDisableBufferReplacementWarning() );
283 bufName << orig->EnumElementName;
284
285 for ( integer replCnt= 0; replCnt< qtyReplacements ; ++replCnt )
286 if ( !replacementPtr->IsType<void>() ) {
287 String64 search("%"); search._( replCnt + 1 );
288 replace.Reset( *( replacementPtr + replCnt) );
289 bufName .SearchAndReplace( search, replace );
290 }
291
292 // search in hashtable
293 {
294 auto it= replacementDeclarations.Find( bufName );
295 if( it != replacementDeclarations.end() )
296 return *it;
297 }
298
299 // not found, we have to create a new one
300 String1K bufComments; ALIB_DBG( bufComments .DbgDisableBufferReplacementWarning() );
301 String128 bufDefaultValue; ALIB_DBG( bufDefaultValue.DbgDisableBufferReplacementWarning() );
302 bufComments << orig->comments;
303 if ( orig->defaultValue.IsNotNull() )
304 bufDefaultValue << orig->defaultValue;
305
306 for ( integer replCnt= 0; replCnt< qtyReplacements ; ++replCnt )
307 if ( !replacementPtr->IsType<void>() ) {
308 String64 search("%"); search._( replCnt + 1 );
309 replace.Reset( *( replacementPtr + replCnt) );
310 bufComments .SearchAndReplace( search, replace );
311 bufDefaultValue.SearchAndReplace( search, replace );
312 }
313
314 //------------------------------------------ create copy -----------------------------------------
315 Declaration* result = GetAllocator()().New<Declaration>();
316 result->EnumElementName.Allocate(GetAllocator(), bufName);
317 result->typeName = orig->typeName;
318 result->defaultValue .Allocate(GetAllocator(), bufDefaultValue);
319 result->comments .Allocate(GetAllocator(), bufComments);
320 replacementDeclarations.EmplaceUnique(result);
321
322 return result;
323}
324
325//##################################################################################################
326// Other tools
327//##################################################################################################
328std::pair<bool,int8_t> Configuration::ParseBooleanToken( const String& pValue ) {
329 int8_t index= 0;
330 Substring value(pValue);
331 if( value.Trim().IsEmpty() )
332 return {false, int8_t(-1)};
333
334 for ( auto& it : BooleanTokens ) {
335 if ( it.first .Match(value) ) return {false, index};
336 if ( it.second.Match(value) ) return {true , index};
337 ++index;
338 }
339 return {false, int8_t(-1)};
340}
341
342AString& Configuration::WriteBooleanToken( bool value, int8_t idxRequested, AString& dest ) {
343 // find the right pair of tokens
344 if(idxRequested < 0)
345 idxRequested= 0;
346
347 auto it= BooleanTokens.begin();
348 for( int8_t index=0 ; index < idxRequested && it != BooleanTokens.end() ; ++index )
349 ++it;
350
351 if( it == BooleanTokens.end() )
352 it= BooleanTokens.begin();
353
354 // use first (false) or second (true) to write
355 (!value ? it->first : it->second).GetExportName( dest );
356 return dest;
357}
358
360DOX_MARKER( [DOX_VARIABLES_DELETE_SAMPLE] )
361// get root node of the tree
362Cursor cs= Root();
363
364// try to walk the givedn path. If a remainder of the string exits, exit.
365if( cs.GoTo(path).IsNotEmpty() )
366 return false;
367
368// now delete the subtree, including the node the cursor represents
369cs.Delete();
370
371// that's it
372return true;
373DOX_MARKER( [DOX_VARIABLES_DELETE_SAMPLE] )
374}
375
376//##################################################################################################
377// Implementation of parsing methods of built-in record types.
378//##################################################################################################
384
385}} // namespace [alib::variables]
386
387
388//##################################################################################################
389// struct AppendableTraits<Variable>
390//##################################################################################################
391#if !DOXYGEN
392
393namespace alib::strings {
394
396 TAString<nchar, lang::HeapAllocator>& target, const variables::Variable& variable ) {
397#if ALIB_CHARACTERS_WIDE
398 String256 name;
399 variable.Name(name);
400 target << name;
401#else
402 variable.Name(target);
403#endif
404
405}
406
408 TAString<wchar, lang::HeapAllocator>& target, const variables::Variable& variable ) {
409#if ALIB_CHARACTERS_WIDE
410 variable.Name(target);
411#else
412 String256 name;
413 variable.Name(name);
414 target << name;
415#endif
416}
417
418
419} // namespace [alib::strings]
420#endif // DOXYGEN
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
#define ALIB_ASSERT_WARNING(cond, domain,...)
#define ALIB_DBG(...)
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
Placeholder data
The data that we encapsulate.
Definition box.hpp:39
bool IsArrayOf() const
Definition box.hpp:555
bool IsType() const
TElementType * UnboxArray() const
Definition box.hpp:796
integer UnboxLength() const
Definition box.hpp:821
StringTree(AllocatorType &allocator, CharacterType pathSeparator)
void InsertPlugin(ConfigurationPlugin *plugin, lang::Responsibility responsibility=lang::Responsibility::KeepWithSender)
void free(void *mem, size_t size)
integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & _(const TAppendable &src)
void DbgDisableBufferReplacementWarning()
Definition tastring.hpp:236
constexpr bool IsEmpty() const
Definition string.hpp:349
TChar CharAtStart() const
Definition string.hpp:417
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:799
constexpr bool IsNotNull() const
Definition string.hpp:339
void Allocate(TAllocator &allocator, const TString< TChar > &copy)
Definition string.hpp:1729
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.hpp:368
constexpr bool IsNull() const
Definition string.hpp:334
bool StartsWith(const TString &needle) const
Definition string.hpp:735
TSubstring & Trim(const TCString< TChar > &whiteSpaces=CStringConstantsTraits< TChar >::DefaultWhitespaces())
bool DeletePath(const String &path)
void notifyListeners(int event, const Variable &variable, const String &variablePath, Priority previousPriority)
AString & WriteBooleanToken(bool value, int8_t index, AString &dest)
ConfigurationPlugin * cliPlugin
A default plugin created and inserted with construction.
ListMA< std::pair< Token, Token >, Recycling::None > BooleanTokens
TypesHashTable types
A hashtable for the registered types. Key is the type name, value is the VMeta singleton.
const Declaration * StoreDeclaration(const Declaration *orig, const Box &replacements)
int MonitorStop(ConfigurationListener *listener)
ConfigurationPlugin * environmentPlugin
A default plugin created and inserted with construction.
ListMA< ListenerRecord > listeners
The list of registered listeners.
void presetImportString(const String &name, const String &value, const StringEscaper *escaper, Priority priority)
Configuration(MonoAllocator &allocator, lang::CreateDefaults createDefaults=lang::CreateDefaults::Yes)
std::pair< bool, int8_t > ParseBooleanToken(const String &src)
void registerListener(ConfigurationListener *listener, lang::ContainerOp insertOrRemove, int event, const Variable *variable, const StringTree::Cursor *subTree, const String &variableName, const String &pathPrefix, const String &pathSubstring)
VMeta * meta
The virtual handler instance for this variable.
VDATA * data
The list hook of values.
ContainerOp
Denotes standard container operations.
@ Insert
Denotes insertions.
CreateDefaults
Denotes whether default entities should be created or not.
@ Yes
Create default values.
void LoadResourcedTokens(camp::Camp &module, const NString &resourceName, strings::util::Token *target, int dbgSizeVerifier, character outerSeparator=',', character innerSeparator=' ')
VData< void * > VDATA
Definition vmeta.hpp:53
containers::detail::StringTreeBase< MonoAllocator, Entry, ConfigNodeHandler, Recycling::Private > TTree
A shortcut to the base class of the base class of class #"Configuration".
Definition alox.cpp:14
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
variables::Variable Variable
Type alias in namespace #"%alib".
strings::TAString< character, PoolAllocator > AStringPA
Type alias in namespace #"%alib".
LocalString< 64 > String64
Type alias name for #"TLocalString;TLocalString<character,64>".
boxing::TBoxes< lang::HeapAllocator > Boxes
Type alias in namespace #"%alib".
Definition boxes.hpp:188
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
boxing::Box Box
Type alias in namespace #"%alib".
Definition box.hpp:1128
boxing::TBoxes< PoolAllocator > BoxesPA
Type alias in namespace #"%alib".
Definition boxes.hpp:195
strings::util::StringEscaper StringEscaper
Type alias in namespace #"%alib".
Definition escaper.hpp:181
strings::TString< character > String
Type alias in namespace #"%alib".
Definition string.hpp:2165
strings::TSubstring< character > Substring
Type alias in namespace #"%alib".
camp::Basecamp BASECAMP
The singleton instance of ALib Camp class #"Basecamp".
Definition basecamp.cpp:2
LocalString< 1024 > String1K
Type alias name for #"TLocalString;TLocalString<character,1024>".
LocalString< 128 > String128
Type alias name for #"TLocalString;TLocalString<character,128>".
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace #"%alib".
Definition boxes.hpp:192
LocalString< 256 > String256
Type alias name for #"TLocalString;TLocalString<character,256>".
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace #"%alib".
strings::util::Token Token
Type alias in namespace #"%alib".
Definition token.hpp:396
String EnumElementName
The name of the enum element.
Definition records.hpp:423
static void Get(String &result, bool isLastField=false)
void operator()(TAString< TChar > &target, const TAppendable &src)
Event
The type of change that imposes the notification of a listener.
Record used to manage registered listeners.
void Parse()
Implementation of #"EnumRecordPrototype::Parse;*".
int Priority
The precedence of an operator in respect to other binary operators.
virtual void destruct(detail::VDATA *memory, PoolAllocator &pool)=0
virtual size_t size()=0
static void FreeNode(TTree::Node &node, TTree &tree)