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/StringTools.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
349 /*
351 static
352 std::string & convertEscaped(std::string &s);
353 */
354
356 bool isLiteral() const;
357
359
364 inline
365 std::ostream & toStream(std::ostream & ostr) const {
367 return ostr;
368 }
369
370 IosFormat iosFormat;
371
372
374
379 template <class T>
380 std::ostream & toStream(std::ostream & ostr, const std::map<std::string,T> & variables, int replace = 0, const VariableFormatter<T> &formatter = VariableFormatter<T>()) const {
381
382 for (const Stringlet & stringlet: *this){
383
384 if (stringlet.isVariable()){
385
386 if (formatter.handle(stringlet, variables, ostr)){
387 // std::cerr << __FILE__ << " ok stringlet variable: " << stringlet << std::endl;
388 // ok, accepted and handled!
389 }
390 else if (replace){
391 //else if (keepUnknowns){ // = "recycle", add back "${variable}";
392 if (replace < 0)
393 ostr << stringlet; // is Variable -> use layout "${variable}";
394 else
395 ostr << (char)replace;
396 // if zero, skip silently (replace with empty string)
397 /*
398 if (replaceChar<0)
399 ostr << '#' << *it << '$'; // is Variable -> use layout "${variable}";
400 else if (replaceChar>1)
401 ostr << (char)replaceChar;
402 */
403 }
404 else { // replace == 0
405 // Skip unknown (unfound) key
406 }
407 }
408 else
409 ostr << stringlet;
410 };
411 return ostr;
412 }
413
415
419 template <class T>
420 std::string toStr(const std::map<std::string,T> &m, int replaceChar = -1, const VariableFormatter<T> & formatter = VariableFormatter<T>()) const {
421 std::stringstream s;
422 toStream(s, m, replaceChar, formatter);
423 return s.str();
424 }
425
427
431 template <class T>
432 void expand(const std::map<std::string,T> &m, const VariableFormatter<T> & formatter=VariableFormatter<T>(), bool clear=false) {
433 for (auto & entry: *this){
434 if (entry.isVariable()){
435 std::stringstream s;
436 if (formatter.handle(entry, m, s)){
437 entry.setLiteral(s.str());
438 }
439 else if (clear) {
440 entry.setLiteral("");
441 }
442 }
443 };
444 }
445
446
448 template <class T>
449 std::ostream & debug(std::ostream & ostr, const std::map<std::string,T> &m ) const {
450
451 const VariableFormatter<T> formatter;
452
453 ostr << "StringMapper '"<< "', RegExp='" << regExp << "', " << size() << " segments:\n";
454 //StringMapper::const_iterator it;
455 for (const auto & entry: *this){
456 ostr << " ";
457 if (entry.isVariable()){
458 ostr << entry << "=";
459 if (!formatter.handle(entry, m, ostr)){
460 ostr << "???";
461 }
462 }
463 else {
464 // Literal
465 ostr << "'" << entry << "'";
466 }
467 ostr << '\n';
468 };
469
470 /*
471 for (StringMapper::const_iterator it = begin(); it != end(); it++){
472 //ostr << *it;
473 ostr << '\t';
474 if (it->isVariable()){
475 //ostr << "VAR: ";
476 typename std::map<std::string, T >::const_iterator mit = m.find(*it);
477 if (mit != m.end())
478 ostr << "\"" << mit->second << "\"";
479 else
480 ostr << *it;
481 }
482 else {
483 ostr << "'"<< *it << "'";
484 //ostr << "LIT: '" << *it << "'\n";
485 }
486 ostr << '\n';
487 //ostr << '\n';
488 }
489 */
490 return ostr;
491 }
492
493
494protected:
495
496 inline
497 void updateRegExp(){
498 std::stringstream sstr;
499 sstr << "^(.*)\\$\\{(" << validChars; // << ")\\}(.*)$";
500 if (formatting){
501 sstr << "(\\|[^}]*)?";
502 }
503 sstr << ")\\}(.*)$";
504 // std::cout << sstr.str() << '\n';
505 regExp.setExpression(sstr.str());
506 }
507
508 StringMapper & parse(const std::string &s, RegExp &r);
509
510 std::string validChars;
511 bool formatting;
512
513 RegExp regExp;
514 // | REG_NEWLINE | RE_DOT_NEWLINE); // | RE_DOT_NEWLINE); // | REG_NEWLINE | RE_DOT_NEWLINE
515
516};
517
518inline
519std::ostream & operator<<(std::ostream & ostr, const StringMapper & strmap){
520 return strmap.toStream(ostr);
521}
522
523
524} // NAMESPACE
525
526#endif /* STRINGMAPPER_H_ */
527
528// 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:325
static const SprinterLayout emptyLayout
Simply concatenate values without punctuation.
Definition Sprinter.h:210
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:107
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
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:40
void expand(const std::map< std::string, T > &m, const VariableFormatter< T > &formatter=VariableFormatter< T >(), bool clear=false)
Expands the variables in StringMapper, turning expanded variables to constants.
Definition StringMapper.h:432
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:380
std::ostream & debug(std::ostream &ostr, const std::map< std::string, T > &m) const
Dumps the list of StringLet's.
Definition StringMapper.h:449
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:365
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:420
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
virtual bool handle(const std::string &key, const map_t &variables, std::ostream &ostr) const
Searches given key in a map, and if found, processes (formats) the value to ostream....
Definition VariableFormatter.h:88
Definition DataSelector.cpp:1277