Sprinter.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 #ifndef DRAIN_SPRINTER
32 #define DRAIN_SPRINTER
33 
34 #include <string.h> // strlen
35 #include <iostream>
36 #include <stdexcept>
37 
38 #include <string>
39 #include <utility> // pair
40 #include <vector>
41 #include <set>
42 #include <list>
43 #include <map>
44 
45 #include <drain/UniTuple.h>
46 
47 
48 
49 namespace drain {
50 
51 
53 
58 class TypeLayoutBase : public UniTuple<char,3>{
59 
60 public:
61 
62  typedef char cstr_t;
63  cstr_t & prefix;
64  cstr_t & separator;
65  cstr_t & suffix;
66 
67  inline
68  TypeLayoutBase(): // chars('\0',3), prefix(chars[0]), separator(chars[1]), suffix(chars[2]){
69  prefix(this->next()), separator(this->next()), suffix(this->next()){
70  }
71 
72  inline
73  bool empty() const { // conside rename isDefined(), esp. if functions used later ?
74  return ((prefix==0) && (separator==0) && (suffix==0));
75  }
76 
77 
78 };
79 
80 struct TypeLayout : public TypeLayoutBase{
81 
82 
84  TypeLayout(cstr_t separator=','){
85  set(0, separator, 0);
86  }
87 
89  /***
90  * Notice that arguments (a,b) invoke set(a,0,b), not set(a,b).
91  */
92  TypeLayout(cstr_t prefix, cstr_t suffix){
93  set(prefix, 0, suffix);
94  }
95 
97  TypeLayout(cstr_t prefix, cstr_t separator, cstr_t suffix){
98  set(prefix, separator, suffix);
99  }
100 
102  TypeLayout(const char layout[4]){ // = "{,}"
103  setLayout(layout);
104  }
105 
106  TypeLayout(const TypeLayout & layout){
107  this->assignSequence(layout);
108  //this->assign(layout); // NOTE: copying element by element, not involving strings possibly containing null char (premature end-or-read).
109  }
110 
112 
120  TypeLayout & setLayout(const char *layout);
121 
122 
123 
124 };
125 
126 
138 
139  // char itemSeparator = ','; // consider as equal sign: KEY:VALUE
140  TypeLayout arrayChars = TypeLayout("[,]");
141  TypeLayout mapChars = TypeLayout("{,}");
142  TypeLayout pairChars = TypeLayout("(,)"); // layout for (key,value), see keyChars
143  TypeLayout stringChars = TypeLayout('"',0,'"'); // TODO: Separate value and sequence layouts?
144  TypeLayout keyChars = TypeLayout(0,0,0); // given a pair = (key,value), defined how the key is .
145  TypeLayout mapPairChars = TypeLayout(0,0,0); // When a map {entry, entry,... } is rendered, defines how
146 
147  // TypeLayout stringChars = TypeLayout("\"\"");
148  // std::string boolTrue = "true";
149  // std::string boolFalse = "false";
150  // std::string nullValue = "null";
151 
152  SprinterLayout(const char *arrayChars="[,]", const char *mapChars="{,}", const char *pairChars="(,)",
153  const char *stringChars=nullptr, const char *keyChars=nullptr, const char *mapPairChars=nullptr){
154  this->arrayChars.setLayout(arrayChars);
155  this->mapChars.setLayout(mapChars);
156  this->pairChars.setLayout(pairChars);
157  if (stringChars)
158  this->stringChars.setLayout(stringChars);
159  if (keyChars)
160  this->keyChars.setLayout(keyChars);
161  if (mapPairChars)
162  this->mapPairChars.setLayout(mapPairChars);
163  }
164 
165  SprinterLayout(const SprinterLayout &layout):
166  arrayChars(layout.arrayChars), mapChars(layout.mapChars), pairChars(layout.pairChars),
167  stringChars(layout.stringChars), keyChars(layout.keyChars), mapPairChars(layout.mapPairChars){
168  }
169 
170 
171  SprinterLayout(char itemSeparator){ // ','
172  arrayChars.separator = itemSeparator;
173  mapChars.separator = itemSeparator;
174  pairChars.separator = itemSeparator;
175  mapPairChars.separator = itemSeparator;
176  }
177 
178 
179 
180 };
181 
182 // Mainly for debugging
183 inline
184 std::ostream & operator<<(std::ostream & ostr, const SprinterLayout & layout){
185  ostr << "arrayChars: " << layout.arrayChars << '\n';
186  ostr << "mapChars: " << layout.mapChars << '\n';
187  ostr << "pairChars: " << layout.pairChars << '\n';
188  ostr << "stringChars: " << layout.stringChars << '\n';
189  ostr << "keyChars: " << layout.keyChars << '\n';
190  ostr << "mapPairChars: " << layout.mapPairChars << '\n';
191  return ostr;
192 }
193 
194 
195 class Sprinter {
196 
197 public:
198 
201 
204 
207 
210 
213 
215 
222 
224  static const SprinterLayout jsLayout;
225 
228 
230 
236  static const SprinterLayout cppLayout;
237 
239 
246 
248  static const SprinterLayout cmdLineLayout; // = {":", ",", "=", ""};
249 
251  // static const SprinterLayout dictLayout; // = {",", "?", "=", ""};
252 
253  static inline
254  void prefixToStream(std::ostream & ostr, const TypeLayout & layout){
255  if (layout.prefix)
256  ostr << layout.prefix;
257  };
258 
259  static inline
260  void separatorToStream(std::ostream & ostr, const TypeLayout & layout){
261  if (layout.separator)
262  ostr << layout.separator;
263  };
264 
265  static inline
266  void suffixToStream(std::ostream & ostr, const TypeLayout & layout){
267  if (layout.suffix)
268  ostr << layout.suffix;
269  };
270 
271 
272  // Complicated, but universal...
273  template <class K, class V>
274  static
275  std::ostream & pairToStream(std::ostream & ostr, const std::pair<K,V> & x, const SprinterLayout & layout){
276 
277  prefixToStream(ostr, layout.pairChars);
278 
279  // EXPERIMENTAL
280  prefixToStream(ostr, layout.keyChars);
281  //toStream(ostr, x.first, layout);
282  ostr << x.first;
283  suffixToStream(ostr, layout.keyChars);
284 
285  separatorToStream(ostr, layout.pairChars);
286 
287  toStream(ostr, x.second, layout);
288 
289  suffixToStream(ostr, layout.pairChars);
290 
291  return ostr;
292  }
293 
295  template <class K, class V>
296  static
297  std::ostream & mapPairToStream(std::ostream & ostr, const std::pair<K,V> & x, const SprinterLayout & layout){
298 
299  prefixToStream(ostr, layout.mapPairChars);
300 
301  // EXPERIMENTAL
302  prefixToStream(ostr, layout.keyChars);
303  //toStream(ostr, x.first, layout);
304  ostr << x.first;
305  suffixToStream(ostr, layout.keyChars);
306 
307  separatorToStream(ostr, layout.mapPairChars);
308 
309  toStream(ostr, x.second, layout); // recursion
310 
311  suffixToStream(ostr, layout.mapPairChars);
312 
313  return ostr;
314  }
315 
316 
317 
319  template <class T>
320  static
321  std::ostream & sequenceToStream(std::ostream & ostr, const T & x, const SprinterLayout & layout){
322  return sequenceToStream(ostr, x, layout.arrayChars, layout);
323  }
324 
325 
327  template <class T>
328  static
329  std::ostream & sequenceToStream(std::ostream & ostr, const T & x, const TypeLayout & myChars, const SprinterLayout & layout){
330 
331  prefixToStream(ostr, myChars);
332 
333  bool first = true;
334  for (const auto & elem: x){
335 
336  if (first)
337  first = false;
338  else
339  separatorToStream(ostr, myChars);
340 
341  toStream(ostr, elem, layout);
342  }
343 
344  suffixToStream(ostr, myChars);
345 
346  return ostr;
347 
348  }
349 
351 
358  template <class M, class K>
359  static
360  std::ostream & mapToStream(std::ostream & ostr, const M & m, const SprinterLayout & layout, const K & keys){
361 
362  prefixToStream(ostr, layout.mapChars); // redesign: layout.startMap(ostr)
363  //if (layout.mapChars.prefix)
364  // ostr << layout.mapChars.prefix;
365 
366  const bool MAP_PAIRS = !layout.mapPairChars.empty();
367 
368  // char sep = 0; // for (const typename K::value_type & key =
369  for (typename K::const_iterator it=keys.begin(); it != keys.end(); ++it){
370 
371  if (it != keys.begin())
372  separatorToStream(ostr, layout.mapChars);
373 
374  typename M::const_iterator mit = m.find(*it);
375  if (mit != m.end()){
376  if (MAP_PAIRS){
377  mapPairToStream(ostr, *mit, layout);
378  }
379  else {
380  toStream(ostr, *mit, layout);
381  }
382  }
383  else {
384  std::cerr << __FILE__ << __FUNCTION__ << ':' << "missing key: " << *it << std::endl;
385  }
386 
387  }
388 
389  suffixToStream(ostr, layout.mapChars); // redesign: layout.endMap(ostr) or layout.mapLayout.putStart(ostr); .putEnd(ostr);
390 
391  return ostr;
392 
393  }
394 
395 
397  //template <class T, bool EXCL>
398  //static
399  //std::ostream & treeToStream(std::ostream & ostr, const Tree<T,EXCL> & tree, const drain::SprinterLayout & layout, short indent){
400  template <class T>
401  static
402  std::ostream & treeToStream(std::ostream & ostr, const T & tree, const drain::SprinterLayout & layout, short indent=0);
403 
404 
406  template <class T>
407  static
408  std::ostream & basicToStream(std::ostream & ostr, const T & x, const TypeLayout & myChars){
409  prefixToStream(ostr, myChars);
410  ostr << x;
411  suffixToStream(ostr, myChars);
412  return ostr;
413  }
414 
415  // std::ostream & mapToStream(std::ostream & ostr, const T & x){
416 
418  template <class T>
419  static
420  std::ostream & toStream(std::ostream & ostr, const std::initializer_list<T> & x, const SprinterLayout & layout = defaultLayout){
421  //ostr << x.size() << '@';
422  return sequenceToStream(ostr, x, layout.arrayChars, layout);
423  }
424 
425  template <class K, class V>
426  static
427  std::ostream & toStream(std::ostream & ostr, const std::pair<K,V> & x, const SprinterLayout & layout = defaultLayout){
428  return pairToStream(ostr, x, layout);
429  }
430 
431  template <class D>
432  static
433  std::ostream & toStream(std::ostream & ostr, const std::vector<D> & x, const SprinterLayout & layout = defaultLayout){
434  return sequenceToStream(ostr, x, layout.arrayChars, layout);
435  }
436 
437  template <class D>
438  static
439  std::ostream & toStream(std::ostream & ostr, const std::list<D> & x, const SprinterLayout & layout = defaultLayout){
440  return sequenceToStream(ostr, x, layout.arrayChars, layout);
441  }
442 
443  template <class D>
444  static
445  std::ostream & toStream(std::ostream & ostr, const std::set<D> & x, const SprinterLayout & layout = defaultLayout){
446  return sequenceToStream(ostr, x, layout.arrayChars, layout);
447  }
448 
449  template <class K, class V>
450  static
451  std::ostream & toStream(std::ostream & ostr, const std::map<K,V> & x, const SprinterLayout & layout = UNSET_LAYOUT){
452 
453  if (&layout == &UNSET_LAYOUT){
454  return sequenceToStream(ostr, x, jsonLayout.mapChars, jsonLayout);
455  }
456  else {
457  return sequenceToStream(ostr, x, layout.mapChars, layout);
458  }
459  //return mapToStream(ostr, x);
460  }
461 
462  template <class D, size_t N>
463  static inline
464  std::ostream & toStream(std::ostream & ostr, const drain::UniTuple<D,N> & x, const SprinterLayout & layout = defaultLayout){
465  return sequenceToStream(ostr, x, layout.arrayChars, layout);
466  }
467 
469  static
470  std::ostream & toStream(std::ostream & ostr, const std::string & x, const SprinterLayout & layout = defaultLayout) {
471  return basicToStream(ostr, x, layout.stringChars);
472  //return ostr << layout.stringChars.prefix << x << layout.stringChars.suffix;
473  }
474 
476  static
477  std::ostream & toStream(std::ostream & ostr, const char *x, const SprinterLayout & layout = defaultLayout) {
478  return basicToStream(ostr, x, layout.stringChars);
479  //return ostr << layout.stringChars.prefix << x << layout.stringChars.suffix;
480  }
481 
483  static
484  std::ostream & toStream(std::ostream & ostr, char c, const SprinterLayout & layout = defaultLayout) { // short int?
485  return basicToStream(ostr, c, layout.stringChars);
486  }
487 
489  static
490  std::ostream & toStream(std::ostream & ostr, bool b, const SprinterLayout & layout = defaultLayout) { // short int?
491  //return basicToStream(ostr, b?"true":"false", layout.stringChars);
492  return ostr << (b ? "true":"false");
493  }
494 
495 
497  template <class D>
498  static
499  std::ostream & toStream(std::ostream & ostr, D *x, const SprinterLayout & layout = defaultLayout) {
500  if (x == nullptr)
501  return ostr << "null";
502  else if (x == 0)
503  return ostr << "null";
504  else
505  return ostr << *x;
506  }
507 
509  template <class D>
510  static
511  std::ostream & toStream(std::ostream & ostr, const D *x, const SprinterLayout & layout = defaultLayout) {
512  return ostr << *x;
513  }
514 
516  template <class D>
517  static
518  std::ostream & toStream(std::ostream & ostr, const D &x, const SprinterLayout & layout = defaultLayout) {
519  // Layout not used
520  return ostr << x;
521  }
522 
523 
524 };
525 
526 
527 
528 template <class T>
529 std::ostream & Sprinter::treeToStream(std::ostream & ostr, const T & tree, const drain::SprinterLayout & layout, short indent){
530 // template <class T, bool EXCL>//
531 // std::ostream & Sprinter::treeToStream(std::ostream & ostr, const Tree<T,EXCL> & tree, const drain::SprinterLayout & layout, short indent){
532 
533  const bool DATA = !tree.data.empty();
534  const bool CHILDREN = !tree.empty();
535 
536  if (! (DATA||CHILDREN)){
537  // Also empty element should return something, here {}, but could be "" or null ?
538  ostr << layout.mapChars.prefix << layout.mapChars.suffix; // '\n';
539  return ostr;
540  }
541 
542  const std::string pad(2*indent, ' ');
543 
544 
545  if (DATA){
546  drain::Sprinter::toStream(ostr, tree.data, layout);
547  //return ostr;
548  /*
549  char sep = 0;
550  for (const auto & entry: tree.data){
551  if (sep){
552  ostr << sep;
553  ostr << '\n';
554  }
555  else {
556  sep = layout.mapChars.separator;
557  }
558  ostr << pad << " " << '"' << entry.first << '"' << layout.pairChars.separator << ' ';
559  drain::Sprinter::toStream(ostr, entry.second, layout);
560  }
561  */
562  if (CHILDREN)
563  ostr << layout.mapChars.separator;
564  ostr << '\n';
565  }
566 
567  ostr << layout.mapChars.prefix << '\n';
568 
569  if (CHILDREN){
570  char sep = 0;
571  for (const auto & entry: tree){
572  if (sep){
573  ostr << sep;
574  ostr << '\n';
575  }
576  else {
577  sep = layout.mapChars.separator;
578  }
579  ostr << pad << " " << '"' << entry.first << '"' << layout.pairChars.separator << ' '; // if empty?
580  treeToStream(ostr, entry.second, layout, indent+1); // recursion
581  }
582  ostr << '\n';
583  }
584 
585  ostr << pad << layout.mapChars.suffix; // << '\n';
586 
587  return ostr;
588 }
589 
590 
591 // MOVE to json files const SprinterLayout Sprinter::layoutJSON;
592 
594 
602 template <class T, class L=SprinterLayout>
603 class Sprintlet : public Sprinter {
604 
605 public:
606 
607  const T & src;
608  SprinterLayout layout;
609 
610  Sprintlet(const T & x, const SprinterLayout & layout = L()) : src(x), layout(layout) {
611  }
612  // Dangerous? Sprinter(const Sprinter<T> & x) : src(x.src){}
613 
614  std::ostream & toStream(std::ostream & ostr) const {
615  return Sprinter::toStream(ostr, src, layout);
616  }
617 
618  std::string str() const {
619  std::stringstream sstr;
620  Sprinter::toStream(sstr, src, layout);
621  return sstr.str();
622  }
623 
624 };
625 
626 
627 template <class T, class L>
628 inline
629 std::ostream & operator<<(std::ostream & ostr, const Sprintlet<T,L> & sp){
630  return sp.toStream(ostr);
631 }
632 
633 
634 // Short cut (constructor wrappers)
635 
636 template <class T>
637 inline
638 Sprintlet<T> sprinter(const T & x, const char *arrayCaps, const char *mapCaps="{,}", const char *pairCaps="(:)", const char *stringCaps="\""){
639  return Sprintlet<T>(x, SprinterLayout(arrayCaps,mapCaps,pairCaps,stringCaps)); // copy const
640 }
641 
642 template <class T>
643 inline
644 Sprintlet<T> sprinter(const T & x, const SprinterLayout & layout = SprinterLayout()){
645  return Sprintlet<T>(x, layout); // copy const
646 }
647 
648 
649 } // drain
650 
651 
652 #endif
653 
Definition: Sprinter.h:195
static std::ostream & toStream(std::ostream &ostr, const char *x, const SprinterLayout &layout=defaultLayout)
Conventional type: C string.
Definition: Sprinter.h:477
static const SprinterLayout xmlAttributeLayout
Like attributes in XML (HTML, SVG, ...) tags.
Definition: Sprinter.h:227
static std::ostream & sequenceToStream(std::ostream &ostr, const T &x, const TypeLayout &myChars, const SprinterLayout &layout)
Print sequence x with myChars, continue recursively with layout.
Definition: Sprinter.h:329
static std::ostream & toStream(std::ostream &ostr, D *x, const SprinterLayout &layout=defaultLayout)
Pointer: redirect to actual target object.
Definition: Sprinter.h:499
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 std::ostream & toStream(std::ostream &ostr, const std::string &x, const SprinterLayout &layout=defaultLayout)
Conventional type: std::string. Notice prefix revealed.
Definition: Sprinter.h:470
static const SprinterLayout pythonLayout
C++ code initializer list style: all objects with {...}, with comma ',' separator.
Definition: Sprinter.h:245
static const SprinterLayout emptyLayout
Simply concatenate values without punctuation.
Definition: Sprinter.h:206
static const SprinterLayout plainLayout
Display plain values, concatenating them with comma (except for strings).
Definition: Sprinter.h:209
static void prefixToStream(std::ostream &ostr, const TypeLayout &layout)
Output Dictionary, which is a list.
Definition: Sprinter.h:254
static const SprinterLayout defaultLayout
Displays objects with {...}, arrays with [...], pairs with (,) and strings without hyphens.
Definition: Sprinter.h:203
static const SprinterLayout jsLayout
JavaScript layout. Like JSON layout, but keys without hyphens.
Definition: Sprinter.h:224
static std::ostream & toStream(std::ostream &ostr, bool b, const SprinterLayout &layout=defaultLayout)
Single char gets styled same way as strings.
Definition: Sprinter.h:490
static std::ostream & toStream(std::ostream &ostr, const D *x, const SprinterLayout &layout=defaultLayout)
Pointer: redirect to actual target object.
Definition: Sprinter.h:511
static std::ostream & treeToStream(std::ostream &ostr, const T &tree, const drain::SprinterLayout &layout, short indent=0)
Write drain::Tree's or any trees that have tree::data[] member.
Definition: Sprinter.h:529
static std::ostream & toStream(std::ostream &ostr, const std::initializer_list< T > &x, const SprinterLayout &layout=defaultLayout)
New (experimental)
Definition: Sprinter.h:420
static const SprinterLayout lineLayout
Put each array and object element on a separate line.
Definition: Sprinter.h:212
static std::ostream & mapPairToStream(std::ostream &ostr, const std::pair< K, V > &x, const SprinterLayout &layout)
If (key,value) pairs needs specific layout in maps, use this. (Compare with list of tuples).
Definition: Sprinter.h:297
static std::ostream & toStream(std::ostream &ostr, char c, const SprinterLayout &layout=defaultLayout)
Single char gets styled same way as strings.
Definition: Sprinter.h:484
static const SprinterLayout cppLayout
C++ code initializer list style: all objects with {...}, with comma ',' separator.
Definition: Sprinter.h:236
static std::ostream & mapToStream(std::ostream &ostr, const M &m, const SprinterLayout &layout, const K &keys)
Given a sequence or subsequence of keys, output values of a map in that order.
Definition: Sprinter.h:360
static const SprinterLayout UNSET_LAYOUT
Marker for unset layout.
Definition: Sprinter.h:200
static const SprinterLayout cmdLineLayout
Simulates how arguments are given to command line options.
Definition: Sprinter.h:248
static std::ostream & basicToStream(std::ostream &ostr, const T &x, const TypeLayout &myChars)
Routine for non-sequence types that may like prefix and suffix, anyway.
Definition: Sprinter.h:408
static const SprinterLayout jsonLayout
Resembles JSON structure: {"a":1,"b":22,"c":3}.
Definition: Sprinter.h:221
static std::ostream & toStream(std::ostream &ostr, const D &x, const SprinterLayout &layout=defaultLayout)
Default, unformatted output for basetypes and types not matching the other templates.
Definition: Sprinter.h:518
Definition: Sprinter.h:603
tuplebase_t & assignSequence(T &sequence, bool LENIENT=false)
Proposed for tuples only; derived classes should not shadow this.
Definition: TupleBase.h:244
Small container for printing style for putting of structured objects (array, maps,...
Definition: Sprinter.h:58
Tuple of N elements of type T.
Definition: UniTuple.h:65
Definition: DataSelector.cpp:1277
Definition: Sprinter.h:137
Definition: Sprinter.h:80
TypeLayout(const char layout[4])
Constructor accepting three-letter chars: {prefix,separator,suffix}.
Definition: Sprinter.h:102
TypeLayout & setLayout(const char *layout)
Set layout with a single string, for example: "{,}" .
Definition: Sprinter.cpp:41
TypeLayout(cstr_t prefix, cstr_t separator, cstr_t suffix)
Constructor.
Definition: Sprinter.h:97
TypeLayout(cstr_t separator=',')
Constructor for simple layout using a separator but no parentheses/braces.
Definition: Sprinter.h:84
TypeLayout(cstr_t prefix, cstr_t suffix)
Constructor.
Definition: Sprinter.h:92