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