TreeXML.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  * TreeXML.h
33  *
34  * Created on: Jun 24, 2012
35  * Author: mpeura
36  */
37 
38 
39 
40 #ifndef TREE_XML
41 #define TREE_XML
42 
43 #include <ostream>
44 
45 #include <drain/Sprinter.h>
46 #include <drain/FlexibleVariable.h>
47 
48 #include "ClassXML.h"
49 #include "Flags.h"
50 #include "ReferenceMap.h"
51 #include "TreeUnordered.h"
52 
53 namespace drain {
54 
55 
60 class StyleXML : public ReferenceMap2<FlexibleVariable> {
61 
62 public:
63 
64  inline
65  StyleXML(){};
66 
67  static const SprinterLayout styleLineLayout;
68  static const SprinterLayout styleRecordLayout;
69  static const SprinterLayout styleRecordLayoutActual;
70 
71 };
72 
73 
74 inline
75 std::ostream & operator<<(std::ostream &ostr, const StyleXML & style){
77  return ostr;
78 }
79 
80 class XML : protected ReferenceMap2<FlexibleVariable> {
81 public:
82 
83 
84  static const int UNDEFINED = 0;
85  static const int COMMENT = 1;
86  static const int CTEXT = 2;
87  static const int STYLE = 3;
88  static const int SCRIPT = 4;
89 
91 
93 
94  // String, still easily allowing numbers through set("id", ...)
95  std::string id;
96 
97  // Consider either/or
98  std::string ctext;
99 
100  std::string url;
101 
102  template <class V>
103  static inline
104  void attribToStream(std::ostream &ostr, const std::string & key, const V &value){
105  ostr << ' ' << key << '=' << '"' << value << '"'; // << ' ';
106  }
107 
108  static int nextID;
109 
111  typedef drain::Path<std::string,'/'> path_t; // consider xml_path_t
112  typedef path_t::elem_t path_elem_t;
113 
114  inline
115  static int getCount(){
116  return nextID;
117  }
118 
120 
123  inline
124  void setId(){
125  link("id", id);
126  }
127 
129  inline
130  void setId(const std::string & s){
131  link("id", id = s);
132  }
133 
134  template <char C='\0', typename ...TT>
135  inline
136  void setId(const TT & ...args) {
137  link("id", id = drain::StringBuilder<C>(args...));
138  }
139 
141  inline
142  const std::string & getId() const {
143  return id;
144  }
145 
146  inline
147  bool empty() const {
148  return map_t::empty();
149  }
150 
151  inline
152  const map_t & getAttributes() const {
153  return *this;
154  };
155 
156  // Maybe controversial. Helps importing sets of variables.
157  inline
158  map_t & getAttributes(){
159  return *this;
160  };
161 
162 
164  // But otherways confusing?
165  virtual inline
166  void setAttribute(const std::string & key, const std::string &value){
167  (*this)[key] = value;
168  }
169 
171  // But otherways confusing?
172  virtual inline
173  void setAttribute(const std::string & key, const char *value){
174  (*this)[key] = value; // -> handleString()
175  }
176 
178  template <class V>
179  inline
180  void setAttribute(const std::string & key, const V & value){
181  (*this)[key] = value; // -> handleString()
182  }
183 
184 
185  inline
186  void remove(const std::string & s){
187  iterator it = this->find(s);
188  if (it != this->end()){
189  this->erase(it);
190  }
191  }
192 
193 
194  inline
195  const drain::FlexibleVariable & get(const std::string & key) const {
196  return (*this)[key];
197  }
198 
199  inline
200  drain::FlexibleVariable & get(const std::string & key){
201  return (*this)[key];
202  }
203 
204 
205  template <class V>
206  inline
207  V get(const std::string & key, const V & defaultValue) const {
208  return map_t::get(key, defaultValue);
209  }
210 
211  inline
212  std::string get(const std::string & key, const char * defaultValue) const {
213  return map_t::get(key, defaultValue);
214  }
215 
217 
218  template <typename ... TT>
219  inline
220  void addClass(const TT &... args) {
221  classList.add(args...);
222  }
223 
224  /*
225  template <typename ... TT>
226  inline
227  void addClass(const std::string & s, const TT &... args) {
228  classList.insert(s);
229  addClass(args...);
230  }
231  */
232 
236  template <class V>
237  inline
238  bool hasClass(const V & cls) const {
239  return classList.has(cls);
240  }
241 
242  inline
243  void removeClass(const std::string & s) {
244  classList.remove(s);
245  }
246 
247  inline
248  void clearClasses(){
249  classList.clear();
250  }
251 
252  // ------------------ Style Class ---------------
253 
254 
255  ClassListXML classList;
256 
257  typedef std::list<path_t> path_list_t;
258 
259  StyleXML style;
260 
262 
273  // This could also be in TreeXMLutilities
274  template <class V>
275  static
276  bool findById(const V & tree, const std::string & tag, typename V::path_t & result, const typename V::path_t & path = path_t());
277 
279 
289  // This could also be in TreeXMLutilities
290  template <class V>
291  static
292  bool findById(const V & tree, const std::string & tag, path_list_t & result, const path_t & path = path_t());
293 
296  template <class V, class T>
297  static
298  bool findByTag(const V & tree, const T & tag, path_list_t & result, const path_t & path = path_t());
299 
301  // This could also be in TreeXMLutilities
306  template <class V, class T>
307  static
308  bool findByTags(const V & tree, const std::set<T> & tags, path_list_t & result, const path_t & path = path_t());
309 
311 
317  template <class T2, class C>
318  static
319  bool findByClass(const T2 & t, const C & cls, std::list<path_elem_t> & result);
320 
322 
327  //template <class V, class E>
328  template <class V, class C>
329  static inline
330  bool findByClass(const V & t, const C & cls, path_list_t & result, const path_t & path = path_t());
331 
332 
333 
334 };
335 
336 
340 template <class T=int>
341 class NodeXML : public XML {
342 
343 public:
344 
345 
347  typedef T elem_t; // consider xml_elem_t
348  elem_t type;
349 
350  typedef NodeXML<T> xml_node_t;
351 
352 
353  typedef UnorderedMultiTree<NodeXML<T>,false, path_t> xml_tree_t;
354 
355 
356  inline
357  NodeXML(const elem_t & t = elem_t(0)){
358  type = t;
359  drain::StringTools::import(++nextID, id);
360  // id = getTag()+id; UNDEF
361  };
362 
363  // Note: use default constructor in derived classes.
364  inline
365  NodeXML(const NodeXML & node){
366  //drain::StringTools::import(++nextID, id);
367  copyStruct(node, node, *this, RESERVE); // This may corrupt (yet unconstructed) object?
368  type = node.getType();
369  drain::StringTools::import(++nextID, id);
370  // id = getTag()+id; UNDEF
371  }
372 
373 
374  inline
375  ~NodeXML(){};
376 
377 
378  typedef std::map<T,std::string> tag_map_t;
379 
380  static
381  tag_map_t tags;
382 
383 
384 
385 
386  virtual
387  void setType(const elem_t &t){
388  type = t;
389  // in derived classes, eg. drain::image::BaseGDAL
390  // warning: case value ‘...’ not in enumerated type
391  }
392 
393  /* Consider this later, for user-defined (not enumerated) tag types.
394  virtual
395  void setType(const std::string & type);
396  */
397 
398  inline
399  const elem_t & getType() const {
400  return type;
401  };
402 
403  inline
404  bool typeIs(const elem_t &t) const {
405  return type == t;
406  };
407 
408  /*
409  template <elem_t E>
410  inline
411  bool typeIs() const {
412  return type == E;
413  };*/
414 
415 
416  // Consider raising these to XML
417  inline
418  bool isComment() const {
419  return typeIs((elem_t)COMMENT);
420  }
421 
422  inline
423  bool isCText() const {
424  return typeIs((elem_t)CTEXT);
425  }
426 
427  inline
428  bool isUndefined() const {
429  return typeIs((elem_t)UNDEFINED);
430  //return ((int)getType() == UNDEFINED);
431  }
432 
433 // protected:
434 
435  // virtual
436  bool isSelfClosing() const {
437 
438  static
439  const std::set<elem_t> l = {(elem_t)SCRIPT};
440  return (l.find(this->getType()) == l.end()); // not in the set
441 
442  //return false;
443  }
444 
445  // template <int N>
446  static inline
447  const std::string & getTag(unsigned int index){
448  typename tag_map_t::const_iterator it = tags.find((elem_t)index);
449  if (it != tags.end()){
450  return it->second;
451  }
452  else {
453  // consider empty
454  static std::string dummy;
455  dummy = drain::StringBuilder<>("UNKNOWN-", index);
456  return dummy;
457  // throw std::runtime_error(drain::StringBuilder<':'>(__FILE__, __FUNCTION__, " tag [", static_cast<int>(index), "] missing from dictionary"));
458  }
459  };
460 
461 
462 
463  inline
464  const std::string & getTag() const {
465  return getTag(type); // risky? converts enum to int .. and back.
466  };
467 
468 
469 
470  // NEW: --------------
471 
472  inline
473  void set(const NodeXML & node){
474  if (isUndefined()){
475  // Should make sense, and be safe. Esp. when exactly same node type, by templating
476  setType(node.getType());
477  }
478  else if (type == STYLE) {
479  drain::Logger mout(__FILE__, __FUNCTION__);
480  mout.suspicious("copying STYLE from node: ", node);
481  }
482  drain::SmartMapTools::setValues<map_t>(getAttributes(), node.getAttributes());
483  }
484 
485  inline
486  void set(const elem_t & type){
487  setType(type);
488  }
489 
490  inline
491  void set(const std::string & s){
492  if (type == STYLE){
493  SmartMapTools::setValues(style, StringTools::trim(s, "; \t\n"), ';', ':');
494  //drain::SmartMapTools::setValues<map_t,true>(getAttributes(), s);
495  }
496  else {
497  setText(s);
498  }
499  }
500 
501  inline
502  void set(const char *s){
503  set(std::string(s));
504  }
505 
506  inline
507  void set(const drain::Castable & s){
508  setText(s);
509  }
510 
511  inline
512  void set(const std::initializer_list<Variable::init_pair_t > &l){
513  // TODO: redirect to set(key,value), for consistency
514  if (type == STYLE){
516  }
517  else {
518  drain::SmartMapTools::setValues<map_t,true>(getAttributes(), l); // add new keys
519  // drain::SmartMapTools::setValues<map_t,false>((map_t &)*this, l); // update only
520  }
521  }
522 
523  template <class X>
524  inline
525  void set(const std::map<std::string, X> & args){
526  // TODO: redirect to set(key,value), for consistency
527  if (type == STYLE){
528  drain::SmartMapTools::setValues(style, args);
529  }
530  else {
531  drain::SmartMapTools::setValues<map_t,true>(getAttributes(), args); // add new keys
532  // drain::SmartMapTools::setValues<map_t,false>((map_t &)*this, l); // update only
533  }
534  }
535 
536 
537  template <class V>
538  inline
539  void set(const std::string & key, const V & value){
540  if (type == STYLE){
541  // Modify collection directly
542  setStyle(key, value);
543  }
544  else if (key == "style"){
545  // Modify collection
546  setStyle(value);
547  }
548  else if (key == "class"){
549  // mout.warn<LOG_DEBUG>("class");
550  std::string cls;
551  drain::StringTools::import(value, cls);
552  addClass(cls);
553  }
554  else {
555  setAttribute(key, value);
556  }
557 
558  }
559 
560 
561 
562  // Check char *
563  inline
564  NodeXML & operator=(const T & x){
565  set(x);
566  return *this;
567  }
568 
569  inline
570  NodeXML & operator=(const Castable &c){
571  setText(c);
572  return *this;
573  }
574 
575 
576  inline
577  NodeXML & operator=(const std::string &s){
578  setText(s);
579  return *this;
580  }
581 
582  inline
583  NodeXML & operator=(const char *s){
584  setText(s);
585  return *this;
586  }
587 
588  inline
589  NodeXML & operator=(const std::initializer_list<std::pair<const char *,const drain::Variable> > &l){
590  set(l);
591  return *this;
592  }
593 
595 
599  inline
600  NodeXML & setComment(const std::string & text = "") {
601 
602  setType((elem_t)COMMENT);
603 
604  if ((int)getType() != COMMENT){ //cross-check
605  throw std::runtime_error(drain::TypeName<NodeXML<T> >::str() + ": COMMENT not supported");
606  }
607 
608  ctext = text;
609  return *this;
610  }
611 
613 
616  template <class S>
617  inline
618  // NodeXML & setText(const std::string & text = "") {
619  NodeXML & setText(const S & value) {
620  if (isUndefined()){
621  setType((elem_t)CTEXT);
622  if (!isCText()){ //cross-check
623  throw std::runtime_error(drain::TypeName<NodeXML<T> >::str() + ": CTEXT not supported");
624  }
625  }
626  else if (typeIs((elem_t)STYLE)){
627  setStyle(value); //
628  // SmartMapTools::updateValues(this->style, value, ';', ':');
629  //
630  }
631  drain::StringTools::import(value, ctext);
632  return *this;
633  }
634 
635  inline
636  NodeXML & setText(const char *value) {
637  setText(std::string(value));
638  return *this;
639  }
640 
641  // ------------------ Style ---------------
642 
643 
644  template <class S>
645  inline
646  void setStyle(const S &value){
647  drain::Logger mout(__FILE__, __FUNCTION__);
648  mout.error("unsupported type ", drain::TypeName<S>::str(), " for value: ", value);
649  }
650 
651  void setStyle(const std::string & value){
652  drain::Logger mout(__FILE__, __FUNCTION__);
653  if (isUndefined()){
654  mout.suspicious("setting style for undefined elem: ", *this);
655  }
656  else if (type == STYLE){
657  mout.warn("setting style for STYLE elem: ", *this);
658  }
659  SmartMapTools::setValues(style, StringTools::trim(value, "; \t\n"), ';', ':');
660  }
661 
662  void setStyle(const char *value){
663  setStyle(std::string(value));
664  }
665 
666  inline
667  void setStyle(const std::string & key, const std::string & value){
668  // const std::initializer_list<Variable::init_pair_t > &l
669  // drain::Logger mout(__FILE__, __FUNCTION__);
670  // mout.special<LOG_WARNING>("Spezial str");
671  this->style[key] = value;
672  }
673 
674 
676 
682  template <class V>
683  inline
684  void setStyle(const std::string & key, const std::initializer_list<V> &l){
685  // const std::initializer_list<Variable::init_pair_t > &l
686  if (type == STYLE){ // typeIs(STYLE) fails
687  drain::Logger mout(__FILE__, __FUNCTION__);
688  mout.warn("Setting style of STYLE? initializer_list<", drain::TypeName<V>::str(), "> = ", sprinter(l)); // , StyleXML::styleLineLayout ?
689  //mout.revised<LOG_WARNING>("Element is not STYLE but ", getTag(), ", initializer_list<", drain::TypeName<V>::str(), "> = ", sprinter(l)); // , StyleXML::styleLineLayout ?
690  }
691  this->style[key] = l;
692  }
693 
694 
696 
699  template <class V>
700  inline
701  void setStyle(const std::string & key, const V & value){
702  // drain::Logger mout(__FILE__, __FUNCTION__);
703  if (type == STYLE){
704  drain::Logger mout(__FILE__, __FUNCTION__);
705  // "reuse" style map as style record map
706  std::stringstream sstr;
707  drain::Sprinter::toStream(sstr, value, StyleXML::styleLineLayout);
708  mout.warn("Setting style of STYLE?, arg type=", drain::TypeName<V>::str());
709  mout.warn("Check results: skipped ", sstr.str());
710  mout.warn("Check results: applied ", value);
711  this->style[key] = value; //sstr.str();
712  }
713  else {
714  // mout.special<LOG_WARNING>("Spezial ", drain::TypeName<V>::str());
715  this->style[key] = value;
716  }
717  }
718 
719 
720  inline
721  void setStyle(const std::initializer_list<std::pair<const char *,const drain::Variable> > &l){
723  }
724 
725 
728 
729 
730 
731  template <class V>
732  static inline
733  std::ostream & docToStream(std::ostream & ostr, const V & tree){
734  V::node_data_t::xml_node_t::docTypeToStream(ostr);
735  V::node_data_t::xml_node_t::toStream(ostr, tree);
736  return ostr;
737  }
738 
740  template <class V>
741  static
742  std::ostream & toStream(std::ostream &ostr, const V & t, const std::string & defaultTag = "", int indent=0);
743 
745 
749  static // virtual
750  inline
751  std::ostream & docTypeToStream(std::ostream &ostr){
752  ostr << "<?xml ";
753  for (const auto & entry: xmldoc_attribs){
754  attribToStream(ostr, entry.first, entry.second);
755  }
756  ostr << "?>";
757  ostr << '\n';
758  return ostr;
759  }
760 
761  // drain::NodeHTML::HTML
762  // typedef std::pair<key_t,xml_node_t> node_pair_t;
763  // TODO: where is this needed?
764  template <int E>
765  static inline
766  const std::pair<key_t,NodeXML<T> > & entry(){
767  static const elem_t elem = (elem_t)E;
768  static const std::pair<key_t,NodeXML<T> > nodeEntry(getTag(E), elem); // note: converts tag (string) to key_t if needed.
769  return nodeEntry;
770  }
771 
772 
773 
774 
775 
776 protected:
777 
778  typedef std::map<std::string,std::string> xmldoc_attrib_map_t;
779 
780  static xmldoc_attrib_map_t xmldoc_attribs;
781 
782  //inline void addClass(){}
783 
784 
785  // TODO: consider TAG from dict?
786  // std::string tag;
787 
788 
789 };
790 
791 /*
792 
793 template <class N>
794 int NodeXML<N>::nextID = 0;
795 
796 template <class T>
797 const int NodeXML<T>::UNDEFINED(0);
798 
799 template <class T>
800 const int NodeXML<T>::COMMENT(1);
801 
802 template <class T>
803 const int NodeXML<T>::CTEXT(2);
804 
805 template <class T>
806 const int NodeXML<T>::STYLE(3);
807 
808 template <class T>
809 const int NodeXML<T>::SCRIPT(4);
810 */
811 
812 typedef drain::UnorderedMultiTree<NodeXML<>,false, NodeXML<>::path_t> TreeXML;
813 
814 // NOTE: template will not match for subclasses of NodeXML<E> because default template will match any class exactly.
815 template <class E, bool EX, class P>
816 struct TypeName< drain::UnorderedMultiTree<NodeXML<E>,EX,P> > {
817 
818  static const std::string & str(){
819  static const std::string name = drain::StringBuilder<>("TreeXML<", TypeName<E>::str(), ">");
820  return name;
821  }
822 
823  static const char* get(){
824  return str().c_str();
825  }
826 };
827 
828 
829 // Experimental.
830 template <>
831 TreeXML & TreeXML::addChild(const TreeXML::key_t & key);
832 
833 
834 
835 //template <class N>
836 template <class T>
837 //bool NodeXML<N>::findById(const T & t, const std::string & id, typename T::path_t & result, const typename T::path_t & path){
838 bool XML::findById(const T & t, const std::string & id, typename T::path_t & result, const typename T::path_t & path){
839 
840  if (t->id == id){
841  result = path;
842  return true;
843  }
844 
845  // Recursion
846  for (const auto & entry: t){
847  if (findById(entry.second, id, result, path_t(path, entry.first))){
848  return true;
849  }
850  }
851 
852  return false;
853  //return !result.empty();
854 }
855 
856 
857 
858 // template <class N> NodeXML<N>
859 template <class T>
860 bool XML::findById(const T & t, const std::string & id, NodeXML<>::path_list_t & result, const path_t & path){
861 
862  if (t->id == id){
863  result.push_back(path);
864  }
865 
866  for (const auto & entry: t){
867  findById(entry.second, id, result, path_t(path, entry.first));
868  }
869 
870  return !result.empty();
871 }
872 
876 //template <class N>
877 template <class T, class N>
878 bool XML::findByTag(const T & t, const N & tag, NodeXML<>::path_list_t & result, const path_t & path){
879 
880  // const T & t = tree(path);
881 
882  if (t->typeIs(tag)){
883  result.push_back(path);
884  }
885 
886  for (const auto & entry: t){
887  findByTag(entry.second, tag, result, path_t(path, entry.first));
888  }
889 
890  //return result;
891  return !result.empty();
892 }
893 
897 //template <class N>
898 template <class T,class N>
899 bool XML::findByTags(const T & t, const std::set<N> & tags, NodeXML<>::path_list_t & result, const path_t & path){
900 
901  // const T & t = tree(path);
902 
903  //if (t->typeIs(tag)){
904  if (tags.count(t->getType()) > 0){
905  result.push_back(path);
906  }
907 
908  for (const auto & entry: t){
909  findByTags(entry.second, tags, result, path_t(path, entry.first));
910  }
911 
912  //return result;
913  return !result.empty();
914 }
915 
916 
917 //template <class N>
918 // template <class T>
919 //bool NodeXML<N>::findByClass(const T & t, const std::string & cls, NodeXML<>::path_list_t & result, const path_t & path){
920 
921 template <class T2, class C> // NodeXML<N>
922 //bool XML::findByClass(const T2 & t, const C & cls, NodeXML<>::path_list_t & result, const path_t & path){
923 bool XML::findByClass(const T2 & t, const C & cls, XML::path_list_t & result, const path_t & path){
924 
925  // drain::Logger mout(__FILE__,__FUNCTION__);
926 
927  if (t->classList.has(cls)){
928  result.push_back(path);
929  }
930 
931  for (const auto & entry: t){
932  // mout.warn(t->get("name", "<name>"), "... continuing to: ", path_t(path, entry.first));
933  findByClass(entry.second, cls, result, path_t(path, entry.first));
934  }
935 
936  return !result.empty();
937 }
938 
939 // template <class N>
940 template <class T2, class C> // NodeXML<N>
941 bool XML::findByClass(const T2 & t, const C & cls, std::list<path_elem_t> & result){
942 
943  for (const auto & entry: t){
944  if (entry.second->hasClass(cls)){
945  result.push_back(entry.first);
946  }
947  }
948 
949  return !result.empty();
950 }
951 
952 
953 
955 
958 template <class N>
959 inline
960 std::ostream & operator<<(std::ostream &ostr, const NodeXML<N> & node){
961 
962  //ostr << node.getTag() << '<' << (unsigned int)node.getType() << '>' << ' ';
963  ostr << '<' << node.getTag() << '>' << ' ';
964 
965  // drain::Sprinter::toStream(ostr, node.getAttributes(), drain::Sprinter::jsonLayout);
966  // drain::Sprinter::toStream(ostr, node.getAttributes().getMap(), drain::Sprinter::jsonLayout);
967  //
968  if (!node.getAttributes().empty()){
970  ostr << ' ';
971  }
972  if (!node.classList.empty()){
973  //ostr << '['; // has already braces []
974  //drain::Sprinter::toStream(ostr, node.classList, drain::Sprinter::pythonLayout);
975  drain::Sprinter::toStream(ostr, node.classList, ClassListXML::layout);
976  //ostr << ']' << ' ';
977  ostr << ' ';
978  }
979  if (!node.style.empty()){
980  ostr << '{';
981  drain::Sprinter::toStream(ostr, node.style);
982  ostr << '}' << ' ';
983  }
984  if (!node.ctext.empty()){
985  ostr << "'";
986  if (node.ctext.length() > 20){
987  ostr << node.ctext.substr(0, 15);
988  }
989  else {
990  ostr << node.ctext;
991  }
992  ostr << "'";
993  }
994  return ostr;
995 }
996 
998 
1004 template <class N>
1005 template <class T>
1006 std::ostream & NodeXML<N>::toStream(std::ostream & ostr, const T & tree, const std::string & defaultTag, int indent){
1007 
1008 
1009  const typename T::container_t & children = tree.getChildren();
1010 
1011  // Indent
1012  //std::fill_n(std::ostream_iterator<char>(ostr), 2*indent, ' ');
1013  std::string fill(2*indent, ' ');
1014  ostr << fill;
1015 
1016  // Start dag
1017  if (tree->isComment()){
1018  ostr << "<!-- " << tree->getTag() << ' ' << tree->ctext; // << " /-->\n";
1019  }
1020  else if (tree->isCText()){
1021  ostr << tree->ctext; // << " /-->\n";
1022  }
1023  else if (tree->getTag().empty())
1024  ostr << '<' << defaultTag; // << ' ';
1025  else {
1026  ostr << '<' << tree->getTag(); // << ' ';
1027  // TODO check GDAL XML
1028  //if (!defaultTag.empty())
1029  // attribToStream(ostr, "name", defaultTag);
1030  }
1031 
1032  if (tree->typeIs((elem_t)STYLE)){
1033  //ostr << ' ';
1034  attribToStream(ostr, "data-mode", "experimental");
1035  // Sprinter::sequenceToStream(ostr, tree->style, StyleXML::styleRecordLayout);
1036  // ostr << "\n /-->";
1037  }
1038  else if (!tree->isCText()){
1039  //char sep=' ';
1040  if (!tree->classList.empty()){
1041  ostr << " class=\"";
1042  drain::Sprinter::toStream(ostr, tree->classList, ClassListXML::layout);
1043  // std::copy(tree->classList.begin(), tree->classList.end(), std::ostream_iterator<std::string>(ostr, " "));
1044  ostr << '"'; //ostr << "\"";
1045  }
1046 
1047  // Iterate attributes - note: also for comment
1048  // Checking empties, so Sprinter::toStream not applicable
1049  for (const typename T::node_data_t::key_t & key: tree.data.getKeyList()){
1050  if (!tree.data[key].empty()){
1051  std::stringstream sstr;
1052  sstr << tree.data[key]; // consider checking 0, not only empty string "".
1053  if (!sstr.str().empty()){
1054  attribToStream(ostr, key, sstr.str());
1055  }
1056  }
1057  //ostr << ' ';
1058  }
1059 
1060  // TAG style
1061  if (!tree->style.empty()){
1062  ostr << " style=\"";
1063  Sprinter::sequenceToStream(ostr, tree->style, StyleXML::styleLineLayout);
1064  //Sprinter::mapToStream(ostr, tree->style, StyleXML::styleLineLayout);
1065  ostr << '"'; // << ' ';
1066  }
1067 
1068 
1069  }
1070  else {
1071  if (!tree.data.empty()){
1072  // ??
1073  }
1074  }
1075 
1076  // END TAG
1077  if (tree->isComment()){
1078  ostr << " /-->\n";
1079  }
1080  else if (tree->isCText()){
1081  ostr << '\n';
1082  }
1083  else if (tree.data.isSelfClosing() &&
1084  (!tree->typeIs((elem_t)STYLE)) && (!tree->typeIs((elem_t)SCRIPT)) &&
1085  (children.empty()) && tree->ctext.empty() ){ // OR no ctext!
1086  // close TAG
1087  ostr << "/>\n";
1088  //ostr << '/' << '>';
1089  //ostr << '\n';
1090  }
1091  else {
1092  // close starting TAG ...
1093  ostr << '>';
1094 
1095  // ... and write contents
1096 
1097  /*
1098  if (!tree->style.empty()){
1099  ostr << "<!-- STYLE? ";
1100  drain::Sprinter::toStream(ostr, tree->style.getMap(), drain::Sprinter::xmlAttributeLayout);
1101  ostr << "/-->\n";
1102  }
1103  */
1104 
1105  if (tree->typeIs((elem_t)STYLE)){
1106  // https://www.w3.org/TR/xml/#sec-cdata-sect
1107  // ostr << "<![CDATA[ \n";
1108  if (!tree->ctext.empty()){
1109  // TODO: indent
1110  ostr << fill << tree->ctext << " /* CTEXT? */" << '\n';
1111  }
1112  if (!tree->getAttributes().empty()){
1113  ostr << "\n\t /* <!-- DISCARDED attribs ";
1114  drain::Logger mout(__FILE__,__FUNCTION__);
1115  mout.warn("STYLE elem contains attributes, probably meant as style: ", tree.data);
1116  Sprinter::toStream(ostr, tree->getAttributes()); //, StyleXML::styleRecordLayout
1117  ostr << " /--> */" << '\n';
1118  }
1119  if (!tree->style.empty()){
1120  ostr << fill << "/** style obj **/" << '\n';
1121  for (const auto & attr: tree->style){
1122  ostr << fill << " ";
1123  Sprinter::pairToStream(ostr, attr, StyleXML::styleRecordLayout); // {" :;"}
1124  //attr.first << ':' attr.first << ':';
1125  ostr << '\n';
1126  }
1127  // ostr << fill << "}\n";
1128  // Sprinter::sequenceToStream(ostr, entry.second->getAttributes(), StyleXML::styleRecordLayoutActual);
1129  // ostr << '\n';
1130  }
1131  ostr << '\n';
1132  // ostr << fill << "<!-- elems /-->" << '\n';
1133  ostr << fill << "/* elems */" << '\n';
1134  for (const auto & entry: tree.getChildren()){
1135  if (!entry.second->ctext.empty()){
1136  //ostr << fill << "<!-- elem("<< entry.first << ") ctext /-->" << '\n';
1137  ostr << fill << " " << entry.first << " {" << entry.second->ctext << "} /* CTEXT */ \n";
1138  }
1139  if (!entry.second->getAttributes().empty()){
1140  //ostr << fill << "<!-- elem("<< entry.first << ") attribs /-->" << '\n';
1141  ostr << fill << " " << entry.first << " {\n";
1142  for (const auto & attr: entry.second->getAttributes()){
1143  ostr << fill << " ";
1144  ostr << attr.first << ':' << attr.second << ';';
1145  //Sprinter::pairToStream(ostr, attr, StyleXML::styleLineLayout); // {" :;"}
1146  // Sprinter::pairToStream(ostr, attr, StyleXML::styleRecordLayout); // {" :;"}
1147  // attr.first << ':' attr.first << ':';
1148  ostr << '\n';
1149  }
1150  ostr << fill << " }\n";
1151  //Sprinter::sequenceToStream(ostr, entry.second->getAttributes(), StyleXML::styleRecordLayoutActual);
1152  ostr << '\n';
1153  }
1154  // Sprinter::sequenceToStream(ostr, entry.second->style, StyleXML::styleRecordLayout);
1155  }
1156  ostr << "\n"; // end CTEXT
1157  //ostr << " ]]>\n"; // end CTEXT
1158  // end STYLE defs
1159  }
1160  else {
1161 
1162  if (tree->ctext.empty())
1163  ostr << '\n'; // TODO nextline
1164  else
1165  ostr << tree->ctext;
1166 
1168  for (const auto & entry: children){
1169  toStream(ostr, entry.second, entry.first, indent+1); // no ++
1170  //ostr << *it;
1171  }
1172  // add end </TAG>
1173 
1174  }
1175 
1176  if (tree->typeIs((elem_t)STYLE) || !children.empty()){
1177  ostr << fill;
1178  //std::fill_n(std::ostream_iterator<char>(ostr), 2*indent, ' ');
1179  }
1180 
1181  ostr << '<' << '/' << tree->getTag() << '>';
1182  ostr << '\n'; // TODO nextline
1183 
1184  //if (tree.data.id >= 0)
1185  // ostr << "<!-- " << tree.data.id << " /-->\n";
1186  }
1187  return ostr;
1188 }
1189 
1190 
1191 
1192 
1193 inline
1194 std::ostream & operator<<(std::ostream &ostr, const TreeXML & t){
1195  // DOC def? TODO: preamble/prologToStream()
1196  TreeXML::node_data_t::docTypeToStream(ostr); // must be member, to support virtual?
1197  TreeXML::node_data_t::toStream(ostr, t, "");
1198  return ostr;
1199 }
1200 
1201 
1202 } // drain::
1203 
1204 #endif /* TREEXML_H_ */
1205 
Definition: Castable.h:76
void add(const std::string &arg, const TT &... args)
Add one or several classes.
Definition: ClassXML.h:72
static const SprinterLayout layout
Uses spaces as separators.
Definition: ClassXML.h:141
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition: Log.h:310
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition: Log.h:428
Definition: TreeXML.h:341
NodeXML & setText(const S &value)
Assign the text content of this node. If the node type is undefined, set it to CTEXT.
Definition: TreeXML.h:619
NodeXML & setComment(const std::string &text="")
Make this node a comment. Contained tree will not be delete. In current version, attributes will be r...
Definition: TreeXML.h:600
void setStyle(const std::string &key, const std::initializer_list< V > &l)
Set style of an element.
Definition: TreeXML.h:684
T elem_t
Tag type, CTEXT or COMMENT.
Definition: TreeXML.h:347
static std::ostream & toStream(std::ostream &ostr, const V &t, const std::string &defaultTag="", int indent=0)
"Forward definition" of Tree::toOstream
static std::ostream & docTypeToStream(std::ostream &ostr)
Write the XML definition beginning any XML document.
Definition: TreeXML.h:751
void setStyle(const std::string &key, const V &value)
For element/class/id, assign ...
Definition: TreeXML.h:701
static std::ostream & docToStream(std::ostream &ostr, const V &tree)
Definition: TreeXML.h:733
Definition: Path.h:112
A map of references to base type scalars, arrays or std::string; changing values in either are equiva...
Definition: ReferenceMap.h:69
void copyStruct(const ReferenceMap2< FlexibleVariable > &m, const T2 &src, T2 &dst, extLinkPolicy policy=RESERVE)
Experimental. Copies references and values of a structure to another.
Definition: ReferenceMap.h:145
@ RESERVE
Definition: ReferenceMap.h:131
ref_t & link(const std::string &key, F &x)
Associates a map entry with a variable.
Definition: ReferenceMap.h:84
static void setValues(M &dst, const std::map< std::string, S > &srcMap)
Definition: SmartMapTools.h:176
map_t::iterator iterator
Needed?
Definition: SmartMap.h:80
std::string get(const std::string &key, const std::string &defaultValue) const
Retrieves a value, or default value if value is unset.
Definition: SmartMap.h:127
const map_t & getMap() const
Definition: SmartMap.h:206
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 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::initializer_list< T > &x, const SprinterLayout &layout=defaultLayout)
New (experimental)
Definition: Sprinter.h:420
Definition: StringBuilder.h:58
static std::string trim(const std::string &s, const std::string &trimChars=" \t\n\r")
Returns a string without leading and trailing whitespace (or str undesired chars).
Definition: String.cpp:149
Definition: TreeXML.h:60
VariableT is a final class applied through typedefs Variable, Reference and FlexibleVariable.
Definition: VariableT.h:87
Definition: TreeXML.h:80
static bool findByClass(const T2 &t, const C &cls, std::list< path_elem_t > &result)
Finds child elements in an XML structure by class name.
Definition: TreeXML.h:941
void addClass(const TT &... args)
Style class.
Definition: TreeXML.h:220
void setId()
Makes ID a visible attribute.
Definition: TreeXML.h:124
virtual void setAttribute(const std::string &key, const char *value)
Default implementation. Needed for handling units in strings, like "50%" or "640px".
Definition: TreeXML.h:173
static bool findByClass(const V &t, const C &cls, path_list_t &result, const path_t &path=path_t())
Finds elements recursively in an XML structure by class name supplied as an enumeration type.
virtual void setAttribute(const std::string &key, const std::string &value)
Default implementation. Needed for handling units in strings, like "50%" or "640px".
Definition: TreeXML.h:166
const std::string & getId() const
Returns ID of this element. Hopefully a unique ID...
Definition: TreeXML.h:142
static bool findById(const V &tree, const std::string &tag, path_list_t &result, const path_t &path=path_t())
Find the occurrence(s) of given ID using recursive breath-first search.
static bool findByTag(const V &tree, const T &tag, path_list_t &result, const path_t &path=path_t())
static bool findById(const V &tree, const std::string &tag, typename V::path_t &result, const typename V::path_t &path=path_t())
Find the first occurrence of given id using recursive breath-first search.
drain::Path< std::string,'/'> path_t
Tree path type.
Definition: TreeXML.h:111
void setAttribute(const std::string &key, const V &value)
"Final" implementation.
Definition: TreeXML.h:180
static bool findByTags(const V &tree, const std::set< T > &tags, path_list_t &result, const path_t &path=path_t())
"Forward definition"
bool hasClass(const V &cls) const
Definition: TreeXML.h:238
std::string id
Some general-purpose.
Definition: TreeXML.h:95
void setId(const std::string &s)
Makes ID a visible attribute, with a given value.
Definition: TreeXML.h:130
Definition: DataSelector.cpp:1277
Definition: Sprinter.h:137
Definition: Type.h:542
static const std::string name
Default implementation: name returned by std::type_info::name()
Definition: Type.h:558