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