ALib C++ Library
Library Version: 2511 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
27 AString& buf , const StringEscaper* escaper ) {
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 integer idx= orig.IndexOfAny<lang::Inclusion::Include>( substitutionVariableDelimiters, varStart );
53 if ( idx < 0 )
54 idx= orig.Length();
55
56 varLen= idx - varStart;
57 searchStartIdx= idx;
58 } else {
59 integer idx= orig.IndexOf( substitutionVariableEnd, varStart );
60 if (idx < 0 ) {
61 ALIB_DBG( String256 namebuf; )
62 ALIB_WARNING( "VARIABLES", "End of substitution variable not found "
63 "(while start was found). Variable name: ", Name(namebuf) )
64 break;
65 }
66
67 varLen= idx - varStart;
68 searchStartIdx= idx + substitutionVariableEnd.Length();
69 }
70
71 // get variable name string
72 Substring replVarName= orig.Substring( varStart, varLen );
73 if ( replVarName.IsEmpty() ) {
74 buf << substitutionVariableStart;
75 continue;
76 }
77
78 // load replacement variable:
79 // - First we do a try. If this succeeds (the variable is already declared), then we're happy
80 // - If not, we declare it as string. This might read it from a plug-in or from a Preset string!
81 // While we are not sure about the type, we declare it as "S" and then delete it, for the case that
82 // it is not a string! This is the best we can do! For example, the escaper given with the preset
83 // is used.
84 Variable replVar(cfg);
85 if( replVar.Try(replVarName) )
86 replVar.Export( buf, escaper );
87 else {
88 replVar.Declare(replVarName, A_CHAR("S") );
89 if( !replVar.IsDefined() ) {
90 replVar.Delete();
91 continue;
92 }
93 replVar.Export( buf, escaper );
94 replVar.Delete();
95 }
96
97
98 }
99 while( --maxReplacements );
100
101 if( maxReplacements < 50) {
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 auto it= Tree<Configuration>().types.Find(typeName);
112 ALIB_ASSERT_ERROR( it != Tree<Configuration>().types.end(), "VARIABLES",
113 "No Meta-Handler found for given variable type \"{}\".\n"
114 "Probably the type was not registered during bootstrap.\n"
115 "Use macro ALIB_VARIABLES_REGISTER_TYPE in bootstrap phase "
116 "'PrepareConfig' to register your custom types.", typeName)
117 auto* meta= Cursor::Value().meta= *it;
118
119 //-------------------------------------------- declare -------------------------------------------
120 Cursor::Value().data = reinterpret_cast<detail::VDATA*>(Tree<Configuration>().Pool().Alloc( meta->size(), alignof(detail::VDATA)));
121 meta->construct(Cursor::Value().data, Tree<Configuration>().Pool );
122 Cursor::Value().priority= Priority::NONE;
123
124 String256 varName(*this);
125
126 // invoke listeners
127 Tree<Configuration>().notifyListeners( int(ConfigurationListener::Event::Creation),
128 *this,
129 varName,
130 Priority::NONE );
131
132
133 //------------------- check for definitions (plugins, preset or default values) ------------------
134 // Plugins?
135 {
136 String256 buf;
137 for (int i = 0; i < Tree<Configuration>().CountPlugins(); ++i) {
138 auto& plugin= *Tree<Configuration>().GetPlugin(i);
139 auto plPrio= plugin.GetPriority();
140 if( Cursor::Value().priority <= plPrio && plugin.Get(varName, buf) ) {
141 String512 substBuf;
142 Cursor::Value().priority= plPrio;
143 Cursor::Value().meta->imPort( Cursor::Value().data, GetConfiguration(), plugin.GetEscaper(),
144 substitute(buf, substBuf, &plugin.GetEscaper()) );
145 } } }
146
147 // Preset value?
148 auto cursor= Tree<Configuration>().Root();
149 if( cursor.GoToChild(A_CHAR("$PRESETS"))
150 && cursor.GoTo(varName).IsEmpty()
151 && cursor->meta != nullptr )
152 {
153 ALIB_ASSERT_ERROR( Variable(cursor).GetString().IsNotNull(), "VARIABLES",
154 "Internal error. This must never happen. ")
155 ALIB_ASSERT_ERROR( Cursor::Value().priority == Priority::NONE, "VARIABLES",
156 "Internal error. This must never happen. ")
157 StringEscaper voidEscaper;
158 auto* escaper= cursor->declaration ? reinterpret_cast<const StringEscaper*>( cursor->declaration )
159 : &voidEscaper;
160 Cursor::Value().priority= cursor->priority;
161 String512 substBuf;
162 Cursor::Value().meta->imPort( Cursor::Value().data,
164 *escaper,
165 substitute(Variable(cursor), substBuf, escaper) );
166
167 }
168
169 // default value?
170 if( Cursor::Value().priority <= Priority::DefaultValues && defaultValue.IsNotEmpty() ) {
171 StringEscaper escaper;
172 String512 substBuf;
173 Cursor::Value().priority= Priority::DefaultValues;
174 Cursor::Value().meta->imPort( Cursor::Value().data,GetConfiguration(), escaper,
175 substitute( defaultValue, substBuf, &escaper) );
176 }
177
178 // invoke listeners
179 if( IsDefined() )
180 Tree<Configuration>().notifyListeners( int(ConfigurationListener::Event::Definition),
181 *this,
182 varName,
183 Priority::NONE );
184
185} // Variable::initialize()
186
187Variable& Variable::Declare( const String& name , const String& typeName,
188 const String& defaultValue ) {
189 ALIB_ASSERT_ERROR(cmCursor::tree != nullptr, "STRINGTREE",
190 "Invalid Variable. Not associated with a Configuration. Probably a default constructed "
191 "instance.\nCopy or move a valid Variable object before usage.")
192
193 ALIB_ASSERT_ERROR( name.IndexOf('%') <0, "VARIABLES",
194 "Variable name with placeholder(s) given: ", name )
195
196 // Variable existed?
197 GoToRoot();
198 if( GoToCreatedPathIfNotExistent(name) == 0
199 && Cursor::Value().meta != nullptr )
200 {
201 #if ALIB_DEBUG
202 auto it= Tree<Configuration>().types.Find(typeName);
203 if( it == Tree<Configuration>().types.end() ) {
204 ALIB_ERROR( "VARIABLES",
205 "No Meta-Handler found for given variable type \"{}\".\n"
206 "Probably the type was not registered during bootstrap.\n"
207 "Use macro ALIB_VARIABLES_REGISTER_TYPE in bootstrap phase "
208 "'PrepareConfig' to register your custom types.", typeName )
209 }
210 if( *it != Cursor::Value().meta ) {
211 ALIB_ERROR( "VARIABLES",
212 "Variable \"{}\" redeclared with a different typename.\n"
213 "Previous typename: ", Cursor::Value().meta->typeName(), typeName )
214 }
215 #endif
216 return *this;
217 }
218
219 // new variable
220 Cursor::Value().declaration= nullptr;
221 create( typeName, defaultValue );
222 return *this;
223}
224
226 ALIB_ASSERT_ERROR(cmCursor::tree != nullptr, "STRINGTREE",
227 "Invalid Variable. Not associated with a Configuration.\n"
228 "Probably a default constructed instance.\n"
229 "Copy or move a valid Variable object before usage.")
230
231 ALIB_ASSERT_ERROR(decl->Name().IndexOf('%') <0, "VARIABLES",
232 "Variable descriptor \"{}\" with unset placeholders given.", decl->Name() )
233
234 // Variable existed?
235 GoToRoot();
236 if( 0 == GoToCreatedPathIfNotExistent( decl->Name() )) {
237 #if ALIB_DEBUG
238 ALIB_ASSERT_WARNING( GetDeclaration() == nullptr || GetDeclaration() == decl, "CONFIG/VARDECL",
239 "Variable \"{}\" redeclared with different declaration record pointer.\n"
240 "Declaration records should be singletons and their life-time needs to survive\n"
241 "that of the variable. New record will be ignored.", decl->Name() )
242
243 auto it= Tree<Configuration>().types.Find(decl->typeName);
244 if( it == Tree<Configuration>().types.end() ) {
245 ALIB_ERROR( "VARIABLES",
246 "No Meta-Handler found for given variable type \"{}\".\n"
247 "Probably the type was not registered during bootstrap.\n"
248 "Use macro ALIB_VARIABLES_REGISTER_TYPE in bootstrap phase "
249 "'PrepareConfig' to register your custom types.", GetDeclaration()->typeName)
250 }
251 #endif
252 return *this;
253 }
254
255 // new variable
256 Cursor::Value().declaration= decl;
257 create(decl->typeName, decl->DefaultValue() );
258 return *this;
259}
260
261bool Variable::Define(Priority requestedPriority) {
262 if( Cursor::Value().priority > requestedPriority )
263 return false;
264 auto prevPriority= Cursor::Value().priority;
265 Cursor::Value().priority= requestedPriority ;
266 Tree<Configuration>().notifyListeners( int(ConfigurationListener::Event::Definition),
267 *this,
268 nullptr,
269 prevPriority );
270 return true;
271}
272
274 Tree<Configuration>().notifyListeners( int(ConfigurationListener::Event::Deletion),
275 *this,
276 nullptr,
277 GetPriority() );
278 Cursor::Delete();
279 Cursor::node= nullptr;
280}
281
282bool Variable::Try(const String& name, const String& typeName) {
283 ALIB_ASSERT_ERROR(cmCursor::tree != nullptr, "STRINGTREE",
284 "Invalid Variable. Not associated with a Configuration. Probably a default constructed "
285 "instance.\nCopy or move a valid Variable object before usage.")
286
287 // check if this variable is already declared
288 if( Cursor::GoToRoot().GoTo( name ).IsEmpty()
289 && IsDeclared() )
290 return true;
291
292 // check if a preset exists
293 auto cursor= Tree<Configuration>().Root();
294 if( cursor.GoToChild(A_CHAR("$PRESETS")) && cursor.GoTo(name).IsEmpty() ) {
295 ALIB_ASSERT_ERROR( Variable(cursor).GetString().IsNotNull(), "VARIABLES",
296 "Internal error. This must never happen.")
297 Declare( name, typeName );
298 if( Cursor::Value().priority < cursor->priority) {
299 StringEscaper voidEscaper;
300 auto* escaper= cursor->declaration ? reinterpret_cast<const StringEscaper*>( cursor->declaration )
301 : &voidEscaper;
302 String512 substBuf;
303 Cursor::Value().priority= cursor->priority;
304 Cursor::Value().meta->imPort( Cursor::Value().data,
306 *escaper,
307 substitute(Variable(cursor), substBuf, escaper) );
308 }
309 return true;
310 }
311 return false;
312}
313
314bool Variable::Try(const Declaration* decl) {
315 if( Try(decl->Name(), decl->TypeName()) ) {
316 // already declared, add the declaration struct, if not set, yet.
317 ALIB_ASSERT_WARNING( Cursor::Value().declaration== nullptr
318 || Cursor::Value().declaration == decl, "CONFIG/VARDECL",
319 "Variable \"{}\" redeclared with different declaration record pointer.\n"
320 "Declaration records should be singletons and their life-time needs to survive\n"
321 "that of the variable. New record will be ignored.", decl->Name() )
322
323 if( Cursor::Value().declaration== nullptr)
324 Cursor::Value().declaration= decl;
325 return true;
326 }
327
328 return false;
329}
330
331void Variable::Import( const String& src, Priority priority, const StringEscaper* escaper ) {
332 ALIB_ASSERT_ERROR( src.IsNotNull(), "VARIABLES",
333 "Tried to import nulled string for variable \"{}\"", this )
334
335 StringEscaper exVoid;
336 if( !escaper )
337 escaper= &exVoid;
338 if(Define(priority)) {
339 String512 substBuf;
340 getMeta()->imPort( Cursor::Value().data, GetConfiguration(), *escaper,
341 substitute(src, substBuf, escaper) );
342} }
343
344
345} // namespace [alib::variables]
constexpr integer Length() const
Definition string.inl:316
constexpr bool IsEmpty() const
Definition string.inl:365
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.inl:815
constexpr bool IsNotNull() const
Definition string.inl:355
constexpr bool IsNotEmpty() const
Definition string.inl:369
integer IndexOfAny(const TString &needles, integer startIdx=0) const
Definition string.inl:973
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.inl:384
const String & DefaultValue() const
const String & TypeName() const
const String & Name() const
ALIB_DLL bool Define(Priority requestedPriority=Priority::Standard)
Definition variable.cpp:261
ALIB_DLL void Import(const String &src, Priority priority, const StringEscaper *escaper=nullptr)
Definition variable.cpp:331
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:110
bool Try(const String &name)
ALIB_DLL void Delete()
Definition variable.cpp:273
ALIB_DLL Variable & Declare(const String &name, const String &typeName, const String &defaultValue=NULL_STRING)
Definition variable.cpp:187
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:1063
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1067
#define ALIB_ERROR(domain,...)
Definition alib.inl:1062
#define ALIB_DBG(...)
Definition alib.inl:853
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1066
@ Include
Chooses inclusion.
VData< void * > VDATA
Definition vmeta.inl:45
strings::util::StringEscaper StringEscaper
Type alias in namespace alib.
Definition escaper.inl:183
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:2189
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