StringMapper.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood 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 
49 namespace drain {
50 
52 
57 //template<class T>
58 // Todo: rename VariableMapper
59 class Stringlet: public std::string {
60 public:
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 
83 protected:
84 
85  bool isVar;
86 
87 };
88 
89 
90 inline
91 std::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 /*
101 template <class T>
102 class VariableHandler {
103 
104 public:
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 
275 class StringMapper : public std::list<Stringlet> {
276 
277 public:
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 
476 protected:
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 
500 inline
501 std::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
StringMapper & enableFormatting(bool formatting)
Enable variable formatting, followed by pipe '|'.
Definition: StringMapper.h:328
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
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
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::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
A helper class for StringMapper.
Definition: StringMapper.h:59
Stringlet(const Stringlet &s)
Copy constructor.
Definition: StringMapper.h:67
Definition: DataSelector.cpp:1277
DRAIN_VARIABLE Variable
Value container supporting dynamic type.
Definition: Variable.h:63