Loading...
Searching...
No Matches
StringMapper.h
1/*
2
3MIT License
4
5Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26/*
27Part of Rack development has been done in the BALTRAD projects part-financed
28by the European Union (European Regional Development Fund and European
29Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30*/
31
32#ifndef STRINGMAPPER_H_
33#define STRINGMAPPER_H_
34
35#include <drain/Log.h>
36#include <map>
37#include <list>
38#include <iterator>
39#include <sstream>
40
41#include <drain/RegExp.h>
42#include <drain/Sprinter.h>
43#include <drain/String.h>
44
45#include "IosFormat.h"
46#include "Time.h"
47#include "VariableFormatter.h"
48
49namespace drain {
50
52
57//template<class T>
58// Todo: rename VariableMapper
59class Stringlet: public std::string {
60public:
61
62 inline
63 Stringlet(const std::string & s = "", bool isVariable = false) : std::string(s), isVar(isVariable) {
64 };
65
67 Stringlet(const Stringlet & s) : std::string(s), isVar(s.isVar) {
68 }
69
70 inline
71 bool isVariable() const { return isVar; };
72
73 inline
74 void setVariable(bool isVariable=true) { isVar = isVariable; };
75
76 inline
77 void setLiteral(const std::string &s) { assign(s); isVar = false; };
78
79 // Consider!
80 // But has to share variable syntax ${...} with string mapper(s), which recognizes it...
81 // std::ostream & toStream(std::ostream & ostr, std::map<std::string, T> & environment, bool clearMissing=true){}
82
83protected:
84
85 bool isVar;
86
87};
88
89
90inline
91std::ostream & operator<<(std::ostream & ostr, const Stringlet & s) {
92 if (s.isVariable())
93 return ostr << "${" << (const std::string &) s << "}";
94 else
95 return ostr << (const std::string &) s;
96}
97
98
99
100/*
101template <class T>
102class VariableHandler {
103
104public:
105
106 inline virtual
107 ~VariableHandler(){};
108
109 IosFormat iosFormat;
110
111
113 // Return false, if variable not found.
114 // Then, further processors may handle the variable tag (remove, change, leave it).
115 virtual
116 bool handle(const std::string & key, const std::map<std::string,T> & variables, std::ostream & ostr) const {
117
118 drain::Logger mout(__FILE__, __FUNCTION__);
119
120 std::string k,format;
121 drain::StringTools::split2(key, k, format, '|');
122 // mout.attention("split '", key, "' to ", k, " + ", format);
123
124 const typename std::map<std::string,T>::const_iterator it = variables.find(k);
125 if (it == variables.end()) {
126 return false;
127 }
128
129
130 if (format.empty()){
131 iosFormat.copyTo(ostr);
132 //vostr.width(width);
133 //vstd::cerr << __FILE__ << " assign -> " << stringlet << std::endl;
134 //vstd::cerr << __FILE__ << " assign <---- " << mit->second << std::endl;
135 ostr << it->second;
136 return true;
137 }
138 else {
139 // mout.attention("delegating '", k, "' to formatVariable: ", format);
140 return formatVariable(k, variables, format, ostr);
141 }
142
143 }
144
145
146
147 // NOTE: must return false, if not found. Then, further processors may handle the variable tag (remove, change, leave it).
148 virtual
149 bool formatVariable(const std::string & key, const std::map<std::string,T> & variables, const std::string & format, std::ostream & ostr) const {
150
151 drain::Logger mout(__FILE__, __FUNCTION__);
152
153
154 const char firstChar = format.at(0);
155 const char lastChar = format.at(format.size()-1);
156
157 if (firstChar == ':'){
158
159 // mout.attention("substring extraction:", format);
160
161 std::string s;
162 drain::MapTools::get(variables, key, s);
163
164 std::vector<size_t> v;
165 drain::StringTools::split(format, v, ':');
166 size_t pos = 0;
167 size_t count = s.size();
168
169 switch (v.size()) {
170 case 3:
171 count = v[2];
172 // no break
173 case 2:
174 pos = v[1];
175 if (pos >= s.size()){
176 mout.warn("index ", pos, " greater than size (", s.size(), ") of string value '", s, "' of '", key, "'");
177 return true;
178 }
179 count = std::min(count, s.size()-pos);
180 ostr << s.substr(v[1], count);
181 break;
182 default:
183 mout.warn("unsupported formatting '", format, "' for variable '", key, "'");
184 mout.advice("use :startpos or :startpos:count for substring extraction");
185 }
186 return true;
187
188 }
189 else if (firstChar == '%'){
190
191 // mout.attention("string formatting: ", format);
192
193 //else if (format.find('%') != std::string::npos){
194 std::string s;
195 drain::MapTools::get(variables, key, s);
196
197 const size_t BUFFER_SIZE = 256;
198 char buffer[BUFFER_SIZE];
199 buffer[0] = '\0';
200 size_t n = 0;
201
202 switch (lastChar){
203 case 's':
204 n = std::sprintf(buffer, format.c_str(), s.c_str());
205 break;
206 case 'c':
207 n = std::sprintf(buffer, format.c_str(), s.at(0)); // ?
208 break;
209 case 'p':
210 mout.unimplemented("pointer type: ", format);
211 break;
212 case 'f':
213 case 'F':
214 case 'e':
215 case 'E':
216 case 'a':
217 case 'A':
218 case 'g':
219 case 'G':
220 {
221 double d = NAN; //nand();
222 drain::MapTools::get(variables, key, d);
223 // ostr << d << '=';
224 n = std::sprintf(buffer, format.c_str(), d);
225 }
226 break;
227 case 'd':
228 case 'i':
229 case 'o':
230 case 'u':
231 case 'x':
232 case 'X':
233 {
234 int i = 0;
235 drain::MapTools::get(variables, key, i);
236 ostr << i << '=';
237 n = std::sprintf(buffer, format.c_str(), i);
238 }
239 break;
240 default:
241 mout.warn("formatting '", format, "' requested for '", key, "' : unsupported type key: ", lastChar);
242 }
243
244 ostr << buffer;
245 if (n > BUFFER_SIZE){
246 mout.fail("formatting with '", format, "' exceeded buffer size (", BUFFER_SIZE, ')');
247 }
248
249 // mout.warn("time formatting '", format, "' requested for '", k, "' not ending with 'time' or 'date'!");
250 }
251
252 return true;
253 }
254
255
256};
257*/
258
259
260
270
275class StringMapper : public std::list<Stringlet> {
276
277public:
278
280
287 const std::string & format = "",
288 const std::string & validChars = "[a-zA-Z0-9_]+",
289 bool formatting=true
290 ): formatting(formatting)
291 {
292 setValidChars(validChars);
293 regExp.setFlags(REG_EXTENDED);
294 if (!format.empty())
295 parse(format);
296 //regExp.setFlags(REG_EXTENDED); // ORDER? Should be before parse!?
297 };
298
300 StringMapper(const RegExp & regexp, bool formatting=true) : formatting(formatting), regExp(regexp) { // fieldWidth(0),
301 // fillChar('0'),
302 // ((std::list<Stringlet> &)*this) = mapper;
303 }
304
306 StringMapper(const StringMapper & mapper) : std::list<Stringlet>(mapper), formatting(mapper.formatting), regExp(mapper.regExp) { // Buggy: regExp(mapper.regExp) {
307 }
308
309
310 inline
311 StringMapper & setValidChars(const std::string & chars){
312 this->validChars = chars;
313 /*
314 std::stringstream sstr;
315 sstr << "^(.*)\\$\\{(" << chars << ")\\}(.*)$";
316 regExp.setExpression(sstr.str());
317 */
318 updateRegExp();
319 return *this;
320 }
321
323
327 inline
328 StringMapper & enableFormatting(bool formatting){
329 if (this->formatting != formatting){
330 this->formatting = formatting;
331 updateRegExp();
332 }
333 else {
334 this->formatting = formatting;
335 }
336 return *this;
337 }
338
339
340
342
347 StringMapper & parse(const std::string &s, bool convertEscaped = false);
348
350 static
351 std::string & convertEscaped(std::string &s);
352
353
355 bool isLiteral() const;
356
358
363 inline
364 std::ostream & toStream(std::ostream & ostr) const {
366 return ostr;
367 }
368
369 IosFormat iosFormat;
370
371
373
378 template <class T>
379 std::ostream & toStream(std::ostream & ostr, const std::map<std::string,T> & variables, int replace = 0, const VariableFormatter<T> &formatter = VariableFormatter<T>()) const {
380
381 for (const Stringlet & stringlet: *this){
382
383 if (stringlet.isVariable()){
384
385 if (formatter.handle(stringlet, variables, ostr)){
386 // std::cerr << __FILE__ << " ok stringlet variable: " << stringlet << std::endl;
387 // ok, accepted and handled!
388 }
389 else if (replace){
390 //else if (keepUnknowns){ // = "recycle", add back "${variable}";
391 if (replace < 0)
392 ostr << stringlet; // is Variable -> use layout "${variable}";
393 else
394 ostr << (char)replace;
395 // if zero, skip silently (replace with empty string)
396 /*
397 if (replaceChar<0)
398 ostr << '#' << *it << '$'; // is Variable -> use layout "${variable}";
399 else if (replaceChar>1)
400 ostr << (char)replaceChar;
401 */
402 }
403 else { // replace == 0
404 // Skip unknown (unfound) key
405 }
406 }
407 else
408 ostr << stringlet;
409 };
410 return ostr;
411 }
412
414
418 template <class T>
419 std::string toStr(const std::map<std::string,T> &m, int replaceChar = -1, const VariableFormatter<T> & formatter = VariableFormatter<T>()) const {
420 std::stringstream s;
421 toStream(s, m, replaceChar, formatter);
422 return s.str();
423 }
424
426
430 template <class T>
431 void expand(const std::map<std::string,T> &m, bool clear=false) {
432 for (StringMapper::iterator it = begin(); it != end(); it++){
433 if (it->isVariable()){
434 typename std::map<std::string, T >::const_iterator mit = m.find(*it);
435 //std::cerr << __FUNCTION__ << " variable: " << *it << std::endl;
436 if (mit != m.end()){
437 //insert(it, Stringlet(Variable(mit->second).toStr()));
438 it->setLiteral(Variable(mit->second).toStr());
439 //it = erase(it);
440 }
441 else if (clear)
442 it->setLiteral("");
443 //it = erase(it);
444 }
445 };
446 }
447
448
450 template <class T>
451 std::ostream & debug(std::ostream & ostr, const std::map<std::string,T> &m ) const {
452 ostr << "StringMapper '"<< "', RegExp='" << regExp << "', " << size() << " segments:\n";
453 //StringMapper::const_iterator it;
454 for (StringMapper::const_iterator it = begin(); it != end(); it++){
455 //ostr << *it;
456 ostr << '\t';
457 if (it->isVariable()){
458 //ostr << "VAR: ";
459 typename std::map<std::string, T >::const_iterator mit = m.find(*it);
460 if (mit != m.end())
461 ostr << "\"" << mit->second << "\"";
462 else
463 ostr << *it;
464 }
465 else {
466 ostr << "'"<< *it << "'";
467 //ostr << "LIT: '" << *it << "'\n";
468 }
469 ostr << '\n';
470 //ostr << '\n';
471 }
472 return ostr;
473 }
474
475
476protected:
477
478 inline
479 void updateRegExp(){
480 std::stringstream sstr;
481 sstr << "^(.*)\\$\\{(" << validChars; // << ")\\}(.*)$";
482 if (formatting){
483 sstr << "(\\|[^}]*)?";
484 }
485 sstr << ")\\}(.*)$";
486 // std::cout << sstr.str() << '\n';
487 regExp.setExpression(sstr.str());
488 }
489
490 StringMapper & parse(const std::string &s, RegExp &r);
491
492 std::string validChars;
493 bool formatting;
494
495 RegExp regExp;
496 // | REG_NEWLINE | RE_DOT_NEWLINE); // | RE_DOT_NEWLINE); // | REG_NEWLINE | RE_DOT_NEWLINE
497
498};
499
500inline
501std::ostream & operator<<(std::ostream & ostr, const StringMapper & strmap){
502 return strmap.toStream(ostr);
503}
504
505
506} // NAMESPACE
507
508#endif /* STRINGMAPPER_H_ */
509
510// Drain
Stores precision, fillChar and fieldWidth applied by STD streams.
Definition IosFormat.h:45
Definition RegExp.h:58
static std::ostream & sequenceToStream(std::ostream &ostr, const T &x, const SprinterLayout &layout)
Convenience: if sequence type (array, list, set, map) not given, assume array.
Definition Sprinter.h:321
static const SprinterLayout emptyLayout
Simply concatenate values without punctuation.
Definition Sprinter.h:206
A tool for expanding variables embedded in a std::string to literals.
Definition StringMapper.h:275
bool isLiteral() const
Return true, if all the elements are literal.
Definition StringMapper.cpp:114
StringMapper(const StringMapper &mapper)
Copy constructor copies the parsed string and the regExp.
Definition StringMapper.h:306
StringMapper(const std::string &format="", const std::string &validChars="[a-zA-Z0-9_]+", bool formatting=true)
Default constructor.
Definition StringMapper.h:286
static std::string & convertEscaped(std::string &s)
Interpret commond special chars tab '\t' and newline ' '.
Definition StringMapper.cpp:42
StringMapper & parse(const std::string &s, bool convertEscaped=false)
Converts a std::string containing variables like in "Hello, ${NAME}!" to a list of StringLet's.
Definition StringMapper.cpp:53
void expand(const std::map< std::string, T > &m, bool clear=false)
Expands the variables in StringMapper, turning expanded variables to constants.
Definition StringMapper.h:431
std::ostream & toStream(std::ostream &ostr, const std::map< std::string, T > &variables, int replace=0, const VariableFormatter< T > &formatter=VariableFormatter< T >()) const
Expands the variables in the last.
Definition StringMapper.h:379
std::ostream & debug(std::ostream &ostr, const std::map< std::string, T > &m) const
Dumps the list of StringLet's.
Definition StringMapper.h:451
StringMapper(const RegExp &regexp, bool formatting=true)
Initialize with the given RegExp // REMOVE!
Definition StringMapper.h:300
StringMapper & enableFormatting(bool formatting)
Enable variable formatting, followed by pipe '|'.
Definition StringMapper.h:328
std::ostream & toStream(std::ostream &ostr) const
Output a concatenated chain of stringlets: literals as such and variables surrounded with "${" and "}...
Definition StringMapper.h:364
std::string toStr(const std::map< std::string, T > &m, int replaceChar=-1, const VariableFormatter< T > &formatter=VariableFormatter< T >()) const
Expands the variables in the last parsed std::string to a std::string.
Definition StringMapper.h:419
A helper class for StringMapper.
Definition StringMapper.h:59
Stringlet(const Stringlet &s)
Copy constructor.
Definition StringMapper.h:67
Formats variables to output stream.
Definition VariableFormatter.h:71
Definition DataSelector.cpp:1277
DRAIN_VARIABLE Variable
Value container supporting dynamic type.
Definition Variable.h:63