ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
variable.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 !DOXYGEN
10# include "alib/config/vmeta.hpp"
14#endif // !DOXYGEN
15
16namespace alib::config {
17
18const String& Variable::substitute( const String& orig, AString& buf, const StringEscaper* escaper )
19{
21 String substitutionVariableStart= cfg.SubstitutionVariableStart;
22 if( substitutionVariableStart.IsEmpty() )
23 return orig;
24
25 String substitutionVariableEnd = cfg.SubstitutionVariableEnd;
26 String substitutionVariableDelimiters= cfg.SubstitutionVariableDelimiters;
27
28 integer searchStartIdx = 0;
29 int maxReplacements = 50;
30 do
31 {
32 // search start
33 integer repStart= orig.IndexOf( substitutionVariableStart, searchStartIdx );
34 if ( repStart < 0 )
35 break;
36 buf << orig.Substring( searchStartIdx, repStart - searchStartIdx );
37 searchStartIdx = repStart;
38 integer varStart= repStart + substitutionVariableStart.Length();
39
40 integer varLen;
41
42 // search end in two different ways depending on setting of public field "SubstitutionVariableEnd"
43 if ( substitutionVariableEnd.IsEmpty() )
44 {
45 integer idx= orig.IndexOfAny<lang::Inclusion::Include>( substitutionVariableDelimiters, varStart );
46 if ( idx < 0 )
47 idx= orig.Length();
48
49 varLen= idx - varStart;
50 searchStartIdx= idx;
51 }
52 else
53 {
54 integer idx= orig.IndexOf( substitutionVariableEnd, varStart );
55 if (idx < 0 )
56 {
57 ALIB_DBG( String256 namebuf; )
58 ALIB_WARNING( "CONFIG", "End of substitution variable not found (while start was found). "
59 "Variable name: ", Name(namebuf) )
60 break;
61 }
62
63 varLen= idx - varStart;
64 searchStartIdx= idx + substitutionVariableEnd.Length();
65 }
66
67 // get variable name string
68 Substring replVarName= orig.Substring( varStart, varLen );
69 if ( replVarName.IsEmpty() )
70 {
71 buf << substitutionVariableStart;
72 continue;
73 }
74
75 // load replacement variable:
76 // - First we do a try. If this succeeds (the variable is already declared), then we're happy
77 // - If not, we declare it as string. This might read it from a plug-in or from a Preset string!
78 // While we are not sure about the type, we declare it as "S" and then delete it, for the case that
79 // it is not a string! This is the best we can do! For example, the escaper given with the preset
80 // is used.
81 Variable replVar(cfg);
82 if( replVar.Try(replVarName) )
83 replVar.Export( buf, escaper );
84 else
85 {
86 replVar.Declare(replVarName, A_CHAR("S") );
87 if( !replVar.IsDefined() )
88 {
89 replVar.Delete();
90 continue;
91 }
92 replVar.Export( buf, escaper );
93 replVar.Delete();
94 }
95
96
97 }
98 while( --maxReplacements );
99
100 if( maxReplacements < 50)
101 {
102 buf << orig.Substring( searchStartIdx );
103 return buf;
104
105 }
106 return orig;
107}
108
109
110void Variable::create( const String& typeName, const String& defaultValue )
111{
112 auto it= Tree<Configuration>().types.Find(typeName);
113 ALIB_ASSERT_ERROR( it != Tree<Configuration>().types.end(), "CONFIG",
114 "No Meta-Handler found for given variable type . Probably the type was not registered\n"
115 "during bootstrap. Use macro ALIB_CONFIG_VARIABLE_REGISTER_TYPE in bootstrap phase \n"
116 "'PrepareConfig' to register your custom types.\n"
117 "Type name in question: ", typeName)
118 auto* meta= Cursor::Value().meta= *it;
119
120 // -------- declare ----
121 Cursor::Value().data = reinterpret_cast<detail::VDATA*>(Tree<Configuration>().Pool().Alloc( meta->size(), alignof(detail::VDATA)));
122 meta->construct(Cursor::Value().data, Tree<Configuration>().Pool );
123 Cursor::Value().priority= Priority::NONE;
124
125 String256 varName(*this);
126
127 // invoke listeners
129 *this,
130 varName,
131 Priority::NONE );
132
133
134 // ------------------ check for definitions (plugins, preset or default values) ----------------
135 // Plugins?
136 {
137 String256 buf;
138 for (int i = 0; i < Tree<Configuration>().CountPlugins(); ++i)
139 {
140 auto& plugin= *Tree<Configuration>().GetPlugin(i);
141 auto plPrio= plugin.GetPriority();
142 if( Cursor::Value().priority <= plPrio && plugin.Get(varName, buf) )
143 {
144 String512 substBuf;
145 Cursor::Value().priority= plPrio;
146 Cursor::Value().meta->imPort( Cursor::Value().data, GetConfiguration(), plugin.GetEscaper(),
147 substitute(buf, substBuf, &plugin.GetEscaper()) );
148 }
149 }
150 }
151
152 // Preset value?
153 auto cursor= Tree<Configuration>().Root();
154 if( cursor.GoToChild(A_CHAR("$PRESETS"))
155 && cursor.GoTo(varName).IsEmpty()
156 && cursor->meta != nullptr )
157 {
158 ALIB_ASSERT_ERROR( Variable(cursor).GetString().IsNotNull(), "CONFIG",
159 "Internal error. This must never happen. ")
160 ALIB_ASSERT_ERROR( Cursor::Value().priority == Priority::NONE, "CONFIG",
161 "Internal error. This must never happen. ")
162 StringEscaper voidEscaper;
163 auto* escaper= cursor->declaration ? reinterpret_cast<const StringEscaper*>( cursor->declaration )
164 : &voidEscaper;
165 Cursor::Value().priority= cursor->priority;
166 String512 substBuf;
167 Cursor::Value().meta->imPort( Cursor::Value().data,
169 *escaper,
170 substitute(Variable(cursor), substBuf, escaper) );
171
172 }
173
174 // default value?
175 if( Cursor::Value().priority <= Priority::DefaultValues && defaultValue.IsNotEmpty() )
176 {
177 StringEscaper escaper;
178 String512 substBuf;
179 Cursor::Value().priority= Priority::DefaultValues;
180 Cursor::Value().meta->imPort( Cursor::Value().data,GetConfiguration(), escaper,
181 substitute( defaultValue, substBuf, &escaper) );
182 }
183
184 // invoke listeners
185 if( IsDefined() )
187 *this,
188 varName,
189 Priority::NONE );
190
191} // Variable::initialize()
192
193Variable& Variable::Declare( const String& name, const String& typeName, const String& defaultValue )
194{
195 ALIB_ASSERT_ERROR(cmCursor::tree != nullptr, "MONOMEM/STRINGTREE",
196 "Invalid Variable. Not associated with a Configuration. Probably a default constructed instance.\n"
197 "Copy or move a valid Variable object before usage.")
198
199 ALIB_ASSERT_ERROR( name.IndexOf('%') <0, "CONFIG",
200 "Variable name with placeholder(s) given: ", name )
201
202 // Variable existed?
203 GoToRoot();
204 if( GoToCreatedPathIfNotExistent(name) == 0
205 && Cursor::Value().meta != nullptr )
206 {
207 #if ALIB_DEBUG
208 auto it= Tree<Configuration>().types.Find(typeName);
209 if( it == Tree<Configuration>().types.end() )
210 {
211 ALIB_ERROR( "CONFIG",
212 "\n No Meta-Handler found for given variable type . Probably the type was not registered\n"
213 " during bootstrap. Use macro ALIB_CONFIG_VARIABLE_REGISTER_TYPE in bootstrap phase \n"
214 " 'PrepareConfig' to register your custom types.\n"
215 " Type name in question: ", typeName)
216 }
217 if( *it != Cursor::Value().meta )
218 {
219 ALIB_ERROR( "CONFIG",
220 "\n Variable {} redeclared with a different typename.\n"
221 " Previous typename: ", Cursor::Value().meta->typeName() )
222 }
223 #endif
224 return *this;
225 }
226
227 // new variable
228 Cursor::Value().declaration= nullptr;
229 create( typeName, defaultValue );
230 return *this;
231}
232
234{
235 ALIB_ASSERT_ERROR(cmCursor::tree != nullptr, "MONOMEM/STRINGTREE",
236 "Invalid Variable. Not associated with a Configuration. Probably a default constructed instance.\n"
237 "Copy or move a valid Variable object before usage.")
238
239 ALIB_ASSERT_ERROR(decl->Name().IndexOf('%') <0, "CONFIG",
240 "Variable descriptor with unset placeholders given: ", decl->Name() )
241
242 // Variable existed?
243 GoToRoot();
244 if( 0 == GoToCreatedPathIfNotExistent( decl->Name() ))
245 {
246 #if ALIB_DEBUG
247 ALIB_ASSERT_WARNING( GetDeclaration() == nullptr || GetDeclaration() == decl, "CONFIG/VARDECL",
248 "\n Variable redeclared with different declaration record pointer.\n"
249 " Declaration records should be singletons and their life-time needs to survive\n"
250 " that of the variable. New record will be ignored. Variable: ", decl->Name() )
251
252
253 auto it= Tree<Configuration>().types.Find(decl->typeName);
254 if( it == Tree<Configuration>().types.end() )
255 {
256 ALIB_ERROR( "CONFIG",
257 "\n No Meta-Handler found for given variable type . Probably the type was not registered\n"
258 " during bootstrap. Use macro ALIB_CONFIG_VARIABLE_REGISTER_TYPE in bootstrap phase \n"
259 " 'PrepareConfig' to register your custom types.\n"
260 " Type name in question: ", GetDeclaration()->typeName)
261 }
262 #endif
263 return *this;
264 }
265
266 // new variable
267 Cursor::Value().declaration= decl;
268 create(decl->typeName, decl->DefaultValue() );
269 return *this;
270}
271
272bool Variable::Define(Priority requestedPriority)
273{
274 if( Cursor::Value().priority > requestedPriority )
275 return false;
276 auto prevPriority= Cursor::Value().priority;
277 Cursor::Value().priority= requestedPriority ;
279 *this,
280 nullptr,
281 prevPriority );
282 return true;
283}
284
286{
288 *this,
289 nullptr,
290 GetPriority() );
292 Cursor::node= nullptr;
293}
294
295bool Variable::Try(const String& name, const String& typeName)
296{
297 ALIB_ASSERT_ERROR(cmCursor::tree != nullptr, "MONOMEM/STRINGTREE",
298 "Invalid Variable. Not associated with a Configuration. Probably a default constructed instance.\n"
299 "Copy or move a valid Variable object before usage.")
300
301 // check if this variable is already declared
302 if( Cursor::GoToRoot().GoTo( name ).IsEmpty()
303 && IsDeclared() )
304 return true;
305
306 // check if a preset exists
307 auto cursor= Tree<Configuration>().Root();
308 if( cursor.GoToChild(A_CHAR("$PRESETS")) && cursor.GoTo(name).IsEmpty() )
309 {
310 ALIB_ASSERT_ERROR( Variable(cursor).GetString().IsNotNull(), "CONFIG",
311 "Internal error. This must never happen.")
312 Declare( name, typeName );
313 if( Cursor::Value().priority < cursor->priority)
314 {
315 StringEscaper voidEscaper;
316 auto* escaper= cursor->declaration ? reinterpret_cast<const StringEscaper*>( cursor->declaration )
317 : &voidEscaper;
318 String512 substBuf;
319 Cursor::Value().priority= cursor->priority;
320 Cursor::Value().meta->imPort( Cursor::Value().data,
322 *escaper,
323 substitute(Variable(cursor), substBuf, escaper) );
324 }
325 return true;
326 }
327 return false;
328}
329
330bool Variable::Try(const Declaration* decl)
331{
332 if( Try(decl->Name(), decl->TypeName()) )
333 {
334 // already declared, add the declaration struct, if not set, yet.
335 ALIB_ASSERT_WARNING( Cursor::Value().declaration== nullptr || Cursor::Value().declaration == decl,
336 "CONFIG/VARDECL",
337 "\n Variable redeclared with different declaration record pointer.\n"
338 " Declaration records should be singletons and their life-time needs to survive\n"
339 " that of the variable. New record will be ignored. Variable: ", decl->Name() )
340
341 if( Cursor::Value().declaration== nullptr)
342 Cursor::Value().declaration= decl;
343 return true;
344 }
345
346 return false;
347}
348
349void Variable::Import( const String& src, Priority priority, const StringEscaper* escaper )
350{
351 ALIB_ASSERT_ERROR( src.IsNotNull(), "CONFIG",
352 "Tried to import nulled string for variable {!Q}", *this )
353
354 StringEscaper exVoid;
355 if( !escaper )
356 escaper= &exVoid;
357 if(Define(priority))
358 {
359 String512 substBuf;
360 getMeta()->imPort( Cursor::Value().data, GetConfiguration(), *escaper,
361 substitute(src, substBuf, escaper) );
362 }
363}
364
365
366} // namespace [alib::config]
367
const String & DefaultValue() const
const String & Name() const
const String & TypeName() const
const String & substitute(const String &importString, AString &buf, const StringEscaper *escaper)
Definition variable.cpp:18
ALIB_API Variable & Declare(const String &name, const String &typeName, const String &defaultValue=NULL_STRING)
Definition variable.cpp:193
bool Try(const String &name)
AString & Export(AString &dest, const StringEscaper *escaper=nullptr) const
const Declaration * GetDeclaration() const
ALIB_API void Delete()
Definition variable.cpp:285
ALIB_API bool Define(Priority requestedPriority=Priority::Standard)
Definition variable.cpp:272
void create(const String &typeName, const String &defaultValue)
Definition variable.cpp:110
Configuration & GetConfiguration() const
Priority GetPriority() const
ALIB_API void Import(const String &src, Priority priority, const StringEscaper *escaper=nullptr)
Definition variable.cpp:349
SubstringType GoTo(const NameType &path)
integer GoToCreatedPathIfNotExistent(const NameType &path, TArgs &&... args)
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:896
constexpr bool IsEmpty() const
Definition string.hpp:383
constexpr bool IsNotEmpty() const
Definition string.hpp:389
constexpr integer Length() const
Definition string.hpp:326
constexpr bool IsNotNull() const
Definition string.hpp:371
integer IndexOfAny(const TString &needles, integer startIdx=0) const
Definition string.hpp:1090
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.hpp:406
#define ALIB_WARNING(...)
Definition alib.hpp:1268
#define A_CHAR(STR)
#define ALIB_ERROR(...)
Definition alib.hpp:1267
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:1272
#define ALIB_DBG(...)
Definition alib.hpp:390
@ Include
Chooses inclusion.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
@ Definition
A variable was defined or re-defined with the same or a higher priority.
@ Creation
A variable was declared for the first time.
virtual void imPort(detail::VDATA *data, Configuration &cfg, const StringEscaper &escaper, const String &src)=0