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