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 DRAIN_TREE_XML
41 #define DRAIN_TREE_XML
42 
43 #include <ostream>
44 
45 #include "TreeUnordered.h"
46 #include "XML.h"
47 
48 
49 namespace drain {
50 
51 
55 // template <class T=int>
56 template <class T=int>
57 class NodeXML : public XML {
58 
59 public:
60 
61  typedef T xml_tag_t;
62  typedef NodeXML<T> xml_node_t;
63  typedef drain::Path<std::string,'/'> path_t;
64  // typedef path_t::elem_t path_elem_t;
65  typedef UnorderedMultiTree<xml_node_t,false, path_t> xml_tree_t;
66  // typedef UnorderedMultiTree<xml_node_t,false, drain::Path<std::string,'/'> > xml_tree_t;
67  // typedef std::list<path_t> path_list_t; // could be?
68 
69  inline
70  NodeXML(const intval_t & t = intval_t(0)){
71  setType(t);
72  //setId(++nextID);
73  drain::StringTools::import(++nextID, id);
74  };
75 
76  // Note: use default constructor in derived classes.
77  inline
78  NodeXML(const NodeXML & node){
79  //drain::StringTools::import(++nextID, id);
80  // RISKY copyStruct(node, node, *this, map_t::RESERVE); // Needed? setType will handle? And this may corrupt (yet unconstructed) object?
81  // type = node.getType();
82  XML::xmlAssignNode(*this, node);
83  drain::StringTools::import(++nextID, id);
84  // setType(node.getType());
85  // setId(++nextID);
86  // drain::StringTools::import(++nextID, id);
87  }
88 
89 
90  inline
91  ~NodeXML(){};
92 
93 
94  template <class T2> // "final"
95  void setType(const T2 &t){ // DANGER, without cast?
96  type = static_cast<intval_t>(t); // (elem_t)
97  handleType(static_cast<T>(t));
98  // in derived classes, eg. drain::image::BaseGDAL
99  // warning: case value ‘...’ not in enumerated type
100  }
101 
102  /* Consider this later, for user-defined (not enumerated) tag types.
103  virtual
104  void setType(const std::string & type);
105  */
106 
107  inline
108  const intval_t & getType() const {
109  return type;
110  };
111 
112  inline
113  T getNativeType() const {
114  return static_cast<T>(type); // may fail! consider two-way conversion assert
115  };
116 
118 
121  template <class T2, class ...T3>
122  inline
123  bool typeIs(const T2 & arg, const T3... args) const {
124  if (type == static_cast<intval_t>(arg)){
125  return true;
126  }
127  else {
128  return typeIs(args...);
129  }
130  };
131 
132 protected:
133 
134  inline
135  bool typeIs() const {
136  return false;
137  };
138 
139 public:
140 
141  /*
142  template <elem_t E>
143  inline
144  bool typeIs() const {
145  return type == E;
146  };*/
147 
148  inline
149  bool isUndefined() const {
150  return type == UNDEFINED;
151  // return typeIs((elem_t)UNDEFINED);
152  //return ((int)getType() == UNDEFINED);
153  }
154 
155 
156  // Consider raising these to XML
157  inline
158  bool isComment() const {
159  return type == COMMENT;
160  //return typeIs((elem_t)COMMENT);
161  }
162 
163  inline
164  bool isCText() const {
165  return type == CTEXT;
166  // return typeIs((elem_t)CTEXT);
167  }
168 
169  inline
170  bool isStyle() const {
171  return type == STYLE;
172  }
173 
174 // protected:
175 
176  // virtual
177  // TODO: strictly open/closed/flexible?
178  bool isSelfClosing() const { // rename explicit closing
179 
180  static
181  const std::set<intval_t> l = {SCRIPT, STYLE}; // todo, append, and/or generalize...
182 
183  return (l.find(this->getType()) == l.end()); // not in the set
184 
185  //return false;
186  }
187 
191  virtual
192  const std::string & getTag() const {
193  // std::cout << __FILE__ << ':' << __FUNCTION__ << "(const) dict: " << drain::TypeName<T>::str() << std::endl; // << drain::EnumDict<T>::dict
194  // std::cout << __FILE__ << ':' << __FUNCTION__ << ' ' << sprinter(drain::EnumDict<T>::dict) << std::endl; // <<
195  //return drain::EnumDict<T>::dict.getKey((T)type, false); // throw error
196  return drain::EnumDict<T>::getDict().getKey((T)type, false);
197  //static std::string dummyTag = "DUMMY";
198  //return dummyTag;
199 
200  }
201 
205  static inline // needed?
206  const std::string & getTag(const T & type){
207  // std::cout << __FILE__ << ':' << __FUNCTION__ << " non-const dict: " << drain::TypeName<T>::str() << std::endl; // << drain::EnumDict<T>::dict
208  // std::cout << __FILE__ << ':' << __FUNCTION__ << ' '<< sprinter(drain::EnumDict<T>::dict) << std::endl; // <<
209  // return drain::EnumDict<T>::dict.getKey((T)type, false); // throw error
210  return drain::EnumDict<T>::getDict().getKey((T)type, false);
211  // static std::string dummyTag = "DUMMY";
212  // return dummyTag;
213  }
214 
215 
216 
217  // NEW: --------------
218 
219  inline
220  void set(const NodeXML & node){
221  if (isUndefined()){
222  // Should make sense, and be safe. Esp. when exactly same node type, by templating
223  setType(node.getType());
224  }
225  else if (type == STYLE) {
226  drain::Logger mout(__FILE__, __FUNCTION__);
227  mout.suspicious("copying STYLE from node: ", node);
228  }
229  drain::SmartMapTools::setValues<map_t>(getAttributes(), node.getAttributes());
230  }
231 
232  inline
233  void set(const intval_t & type){
234  setType(type);
235  }
236 
237  inline
238  void set(const std::string & s){
239  setText(s);
240  }
241 
242  inline
243  void set(const char *s){
244  set(std::string(s));
245  }
246 
247  inline
248  void set(const drain::Castable & s){
249  setText(s);
250  }
251 
252  /*
253  inline
254  void set(const std::initializer_list<Variable::init_pair_t > &l){
255  // TODO: redirect to set(key,value), for consistency
256  if (type == STYLE){
257  drain::SmartMapTools::setValues(style, l);
258  }
259  else {
260  drain::SmartMapTools::setValues<map_t,true>(getAttributes(), l); // add new keys
261  // drain::SmartMapTools::setValues<map_t,false>((map_t &)*this, l); // update only
262  }
263  }
264  */
265 
266  inline
267  //void set(const std::initializer_list<std::pair<const char *,const char *> > & args){
268  void set(const std::initializer_list<std::pair<const char *,const Variable> > & args){
269  // TODO: redirect to set(key,value), for consistency?
270  if (type == STYLE){
271  drain::Logger mout(__FILE__, __FUNCTION__);
272  mout.deprecating("Setting attributes/style of a STYLE element.");
273  setStyle(args);
274  /*
275  for (const auto & entry: l){
276  style[entry.first] = entry.second;
277  }
278  */
279  // drain::SmartMapTools::setValues(style, l);
280  }
281  else {
282  for (const auto & entry: args){
283  getAttributes()[entry.first] = entry.second;
284  }
286  // drain::SmartMapTools::setValues<map_t,false>((map_t &)*this, l); // update only
287  }
288  }
289 
290  template <class V>
291  inline
292  void set(const std::map<std::string, V> & args){
293  // TODO: redirect to set(key,value), for consistency
294  if (type == STYLE){
295  drain::Logger mout(__FILE__, __FUNCTION__);
296  mout.deprecating("Setting attributes/style of a STYLE element: ", args);
297  //drain::SmartMapTools::setValues(style, args);
298  setStyle(args);
299  /*
300  for (const auto & entry: args){
301  style[entry.first] = entry.second;
302  }
303  */
304  }
305  else {
306  drain::SmartMapTools::setValues<map_t,true>(getAttributes(), args); // add new keys
307  // drain::SmartMapTools::setValues<map_t,false>((map_t &)*this, l); // update only
308  }
309  }
310 
311 
312  template <class V>
313  inline
314  void set(const std::string & key, const V & value){
315  if (type == STYLE){
316  // Modify collection directly
317  drain::Logger mout(__FILE__, __FUNCTION__);
318  mout.deprecating("Setting style as attributes of a STYLE element: ", key, ':', value);
319  setStyle(key, value);
320  }
321  else if (key == "style"){
322  // Modify collection
323  // setStyle(value);
324  drain::Logger mout(__FILE__, __FUNCTION__);
325  mout.unimplemented("Setting style as attribute: \"style\"=", value);
326  //setStyle(key, value);
327  }
328  else if (key == "class"){
329  // mout.warn<LOG_DEBUG>("class");
330  std::string cls;
331  drain::StringTools::import(value, cls);
332  addClass(cls);
333  }
334  else {
335  setAttribute(key, value);
336  }
337 
338  }
339 
340  inline
341  NodeXML & operator=(const NodeXML & node){
342  return XML::xmlAssignNode(*this, node);
343  }
344 
345  // Check char *
346  inline
347  NodeXML & operator=(const xml_tag_t & x){ // needed? possibly with tree(int x) ?
348  //set(x);
349  setType(x);
350  return *this;
351  }
352 
353  inline
354  NodeXML & operator=(const Castable &c){
355  setText(c);
356  return *this;
357  }
358 
359 
360  inline
361  NodeXML & operator=(const std::string &s){
362  setText(s);
363  return *this;
364  }
365 
366  inline
367  NodeXML & operator=(const char *s){
368  setText(s);
369  return *this;
370  }
371 
372  inline
373  NodeXML & operator=(const std::initializer_list<std::pair<const char *,const drain::Variable> > &l){
374  set(l);
375  return *this;
376  }
377 
378 
381 
382  std::ostream & nodeToStream(std::ostream & ostr) const;
383 
384 
385 
386  template <class V>
387  static inline
388  std::ostream & docToStream(std::ostream & ostr, const V & tree){
389  V::node_data_t::xml_node_t::docTypeToStream(ostr);
390  V::node_data_t::xml_node_t::toStream(ostr, tree);
391  return ostr;
392  }
393 
395  template <class V>
396  static
397  std::ostream & toStream(std::ostream &ostr, const V & t, const std::string & defaultTag = "", int indent=0);
398 
400 
404  static // virtual
405  inline
406  std::ostream & docTypeToStream(std::ostream &ostr){
407  ostr << "<?xml ";
408  for (const auto & entry: xmldoc_attribs){
409  xmlAttribToStream(ostr, entry.first, entry.second);
410  }
411  ostr << "?>";
412  ostr << '\n';
413  return ostr;
414  }
415 
416  // drain::NodeHTML::HTML
417  // typedef std::pair<key_t,xml_node_t> node_pair_t;
418  // TODO: where is this needed?
419  /*
420  template <int E>
421  static inline
422  const std::pair<key_t,NodeXML<intval_t> > & entry(){
423  static const intval_t elem = (intval_t)E;
424  static const std::pair<key_t,NodeXML<intval_t> > nodeEntry(getTag(E), elem); // note: converts tag (string) to key_t if needed.
425  return nodeEntry;
426  }
427  */
428 
429  typedef std::map<xml_tag_t,xml_tag_t> xml_default_elem_map_t;
430  static const xml_default_elem_map_t xml_default_elems;
431 
432 protected:
433 
435 
438  virtual inline
439  void handleType(const T & type){
440  if ((int)type != 0){
441  drain::Logger(__FILE__, __FUNCTION__).reject("handleType( ", (int)type, " ) - this is available only as specialized, by inherited classed like SVG, HTML?");
442  }
443  // DANGER, without cast?
444  // std::cerr << __FILE__ << ':' << __FUNCTION__ << " unimplemented? " << type << '=' << std::endl;
445  // std::cerr << __FILE__ << ':' << __FUNCTION__ << " dict: " << drain::sprinter(drain::EnumDict<T>::dict) << std::endl;
446  }
447 
448 
450  typedef std::map<std::string,std::string> xmldoc_attrib_map_t;
451  static xmldoc_attrib_map_t xmldoc_attribs;
452 
453 
454 };
455 
456 /*
457  Impossible. Cannot construct final object, for example members are not available for #link() .
458 template <>
459 inline
460 void NodeXML<int>::handle Type(const int & type){
461  // DANGER, without cast?
462  std::cerr << __FILE__ << ':' << __FUNCTION__ << " unimplemented? " << type << '=' << std::endl;
463  std::cerr << __FILE__ << ':' << __FUNCTION__ << " dict: " << drain::sprinter(drain::EnumDict<int,XML>::dict) << std::endl;
464 }
465 */
466 
467 template <>
468 inline
469 const std::string & NodeXML<int>::getTag() const {
470  return drain::EnumDict<int,XML>::dict.getKey(this->type, false);
471 }
472 
473 typedef NodeXML<>::xml_tree_t TreeXML;
474 //typedef drain::UnorderedMultiTree<NodeXML<>,false, NodeXML<>::path_t> TreeXML;
475 
476 // NOTE: template will not match for subclasses of NodeXML<E> because default template will match any class exactly.
477 template <class E, bool EX, class P>
478 struct TypeName< drain::UnorderedMultiTree<NodeXML<E>,EX,P> > {
479 
480  static const std::string & str(){
481  static const std::string name = drain::StringBuilder<>("TreeXML<", TypeName<E>::str(), ">");
482  return name;
483  }
484 
485 };
486 
487 /* This could be it, unless already defined...
488 template <class N, bool EX, class P>
489 inline
490 drain::UnorderedMultiTree<N,EX,P> & drain::UnorderedMultiTree<N,EX,P>::addChild(const drain::UnorderedMultiTree<N,EX,P>::key_t & key){
491  return XML::xmlAddChild(*this, key);
492 }
493 */
494 
495 /* doesnt work
496 template <class E, bool EX, class P>
497 inline
498 drain::UnorderedMultiTree<NodeXML<E>,EX,P> & drain::UnorderedMultiTree<NodeXML<E>,EX,P>::addChild(const std::string & key){
499  return XML::xmlAddChild(*this, key);
500 }
501 */
502 
503 /*
504 template <class N, bool EX, class P>
505 template <typename K> // for K (path elem arg)
506 const typename drain::UnorderedMultiTree<N,EX,P>::key_t & drain::UnorderedMultiTree<N,EX,P>::getKey(const K & key){
507  return drain::EnumDict<K>::dict.getKey(key, false);
508 }
509 */
510 
511 
512 
514 
517 template <class N>
518 std::ostream & NodeXML<N>::nodeToStream(std::ostream &ostr) const {
519 
520  //ostr << node.getTag() << '<' << (unsigned int)node.getType() << '>' << ' ';
521  ostr << '<' << getTag() << '>' << ' ';
522 
523  // drain::Sprinter::toStream(ostr, node.getAttributes(), drain::Sprinter::jsonLayout);
524  // drain::Sprinter::toStream(ostr, node.getAttributes().getMap(), drain::Sprinter::jsonLayout);
525  //
526  if (!getAttributes().empty()){
527  drain::Sprinter::toStream(ostr, getAttributes().getMap(), drain::Sprinter::xmlAttributeLayout);
528  ostr << ' ';
529  }
530  if (!getClasses().empty()){
531  //ostr << '['; // has already braces []
532  //drain::Sprinter::toStream(ostr, node.classList, drain::Sprinter::pythonLayout);
533  drain::Sprinter::toStream(ostr, getClasses(), ClassListXML::layout);
534  //ostr << ']' << ' ';
535  ostr << ' ';
536  }
537  if (!getStyle().empty()){
538  ostr << '{';
539  drain::Sprinter::toStream(ostr, getStyle());
540  ostr << '}' << ' ';
541  }
542  if (!ctext.empty()){
543  ostr << "'";
544  if (ctext.length() > 20){
545  ostr << ctext.substr(0, 15) << "..";
546  }
547  else {
548  ostr << ctext;
549  }
550  ostr << "'";
551  }
552  return ostr;
553 }
554 
555 template <class N>
556 inline
557 std::ostream & operator<<(std::ostream &ostr, const NodeXML<N> & node){
558  return node.nodeToStream(ostr);
559 }
560 
562 
568 template <class T>
569 template <class TR>
570 std::ostream & NodeXML<T>::toStream(std::ostream & ostr, const TR & tree, const std::string & defaultTag, int indent){
571 
572  // TODO: delegate to XML node start/end function, maybe xmlNodeToStream ?
573 
574  const typename TR::container_t & children = tree.getChildren();
575 
576  // Indent
577  //std::fill_n(std::ostream_iterator<char>(ostr), 2*indent, ' ');
578  std::string fill(2*indent, ' ');
579  ostr << fill;
580 
581  // Start dag
582  if (tree->isComment()){
583  ostr << "<!-- " << tree->getTag() << ' ' << tree->ctext; // << " /-->\n";
584  }
585  else if (tree->isCText()){
586  ostr << tree->ctext; // << " /-->\n";
587  }
588  else if (tree->getTag().empty())
589  ostr << '<' << defaultTag; // << ' ';
590  else {
591  ostr << '<' << tree->getTag(); // << ' ';
592  // TODO check GDAL XML
593  //if (!defaultTag.empty())
594  // attribToStream(ostr, "name", defaultTag);
595  }
596 
597  if (tree->typeIs((intval_t)STYLE)){
598  //ostr << ' ';
599  xmlAttribToStream(ostr, "data-mode", "experimental");
600  // Sprinter::sequenceToStream(ostr, tree->style, StyleXML::styleRecordLayout);
601  // ostr << "\n /-->";
602  }
603  else if (!tree->isCText()){
604  //char sep=' ';
605  tree->specificAttributesToStream(ostr);
606  /*
607  if (!tree->classList.empty()){
608  ostr << " class=\"";
609  drain::Sprinter::toStream(ostr, tree->classList, ClassListXML::layout);
610  // std::copy(tree->classList.begin(), tree->classList.end(), std::ostream_iterator<std::string>(ostr, " "));
611  ostr << '"'; //ostr << "\"";
612  }
613  */
614 
615  // Iterate attributes - note: also for comment
616  // Checking empties, so Sprinter::toStream not applicable
617  //for (const typename T::node_data_t::key_t & key: tree.data.getKeyList()){
618  for (const typename TR::node_data_t::key_t & key: tree->getAttributes().getKeyList()){
619  if (!tree.data[key].empty()){
620  std::stringstream sstr;
621  sstr << tree.data[key]; // consider checking 0, not only empty string "".
622  if (!sstr.str().empty()){
623  xmlAttribToStream(ostr, key, sstr.str());
624  }
625  }
626  //ostr << ' ';
627  }
628 
629  // TAG style
630  if (!tree->style.empty()){
631  ostr << " style=\"";
632  Sprinter::sequenceToStream(ostr, tree->style, StyleXML::styleLineLayout);
633  //Sprinter::mapToStream(ostr, tree->style, StyleXML::styleLineLayout);
634  ostr << '"'; // << ' ';
635  }
636 
637 
638  }
639  else {
640  if (!tree.data.empty()){
641  // ??
642  }
643  }
644 
645  // END TAG
646  if (tree->isComment()){
647  ostr << " /-->\n";
648  }
649  else if (tree->isCText()){
650  ostr << '\n';
651  }
652  else if (tree.data.isSelfClosing() &&
653  (!tree->typeIs((intval_t)STYLE)) && (!tree->typeIs((intval_t)SCRIPT)) &&
654  (children.empty()) && tree->ctext.empty() ){ // OR no ctext!
655  // close TAG
656  ostr << "/>\n";
657  //ostr << '/' << '>';
658  //ostr << '\n';
659  }
660  else {
661  // close starting TAG ...
662  ostr << '>';
663 
664  // ... and write contents
665 
666  /*
667  if (!tree->style.empty()){
668  ostr << "<!-- STYLE? ";
669  drain::Sprinter::toStream(ostr, tree->style.getMap(), drain::Sprinter::xmlAttributeLayout);
670  ostr << "/-->\n";
671  }
672  */
673 
674  if (tree->isStyle()){
675  // https://www.w3.org/TR/xml/#sec-cdata-sect
676  // ostr << "<![CDATA[ \n";
677 
678  if (!tree->ctext.empty()){
679  // TODO: indent
680  ostr << fill << tree->ctext << " /* CTEXT? */" << '\n';
681  }
682 
683  if (!tree->getAttributes().empty()){
684  drain::Logger mout(__FILE__,__FUNCTION__);
685  mout.warn("STYLE elem ", tree->getId()," contains attributes, probably meant as style: ", sprinter(tree->getAttributes()));
686  ostr << "\n\t /* <!-- DISCARDED attribs ";
687  Sprinter::toStream(ostr, tree->getAttributes()); //, StyleXML::styleRecordLayout
688  ostr << " /--> */" << '\n';
689  }
690 
691  if (!tree->style.empty()){
692  ostr << fill << "/** style obj **/" << '\n';
693  for (const auto & attr: tree->style){
694  ostr << fill << " ";
695  Sprinter::pairToStream(ostr, attr, StyleXML::styleRecordLayout); // {" :;"}
696  //attr.first << ':' attr.first << ':';
697  ostr << '\n';
698  }
699  // ostr << fill << "}\n";
700  // Sprinter::sequenceToStream(ostr, entry.second->getAttributes(), StyleXML::styleRecordLayoutActual);
701  // ostr << '\n';
702  }
703 
704  ostr << '\n';
705  // ostr << fill << "<!-- elems /-->" << '\n';
706  ostr << fill << "/* elems */" << '\n';
707  for (const auto & entry: tree.getChildren()){
708  if (!entry.second->ctext.empty()){
709  //ostr << fill << "<!-- elem("<< entry.first << ") ctext /-->" << '\n';
710  ostr << fill << " " << entry.first << " {" << entry.second->ctext << "} /* CTEXT */ \n";
711  }
712  if (!entry.second->getAttributes().empty()){
713  //ostr << fill << "<!-- elem("<< entry.first << ") attribs /-->" << '\n';
714  ostr << fill << " " << entry.first << " {\n";
715  for (const auto & attr: entry.second->getAttributes()){
716  ostr << fill << " ";
717  ostr << attr.first << ':' << attr.second << ';';
718  //Sprinter::pairToStream(ostr, attr, StyleXML::styleLineLayout); // {" :;"}
719  // Sprinter::pairToStream(ostr, attr, StyleXML::styleRecordLayout); // {" :;"}
720  // attr.first << ':' attr.first << ':';
721  ostr << '\n';
722  }
723  ostr << fill << " }\n";
724  //Sprinter::sequenceToStream(ostr, entry.second->getAttributes(), StyleXML::styleRecordLayoutActual);
725  ostr << '\n';
726  }
727  // Sprinter::sequenceToStream(ostr, entry.second->style, StyleXML::styleRecordLayout);
728  }
729  ostr << "\n"; // end CTEXT
730  //ostr << " ]]>\n"; // end CTEXT
731  // end STYLE defs
732  }
733  else {
734 
735  if (tree->ctext.empty())
736  ostr << '\n'; // TODO nextline
737  else
738  ostr << tree->ctext;
739 
741  for (const auto & entry: children){
742  toStream(ostr, entry.second, entry.first, indent+1); // no ++
743  //ostr << *it;
744  }
745  // add end </TAG>
746 
747  }
748 
749  if (tree->typeIs((intval_t)STYLE) || !children.empty()){
750  ostr << fill;
751  //std::fill_n(std::ostream_iterator<char>(ostr), 2*indent, ' ');
752  }
753 
754  ostr << '<' << '/' << tree->getTag() << '>';
755  ostr << '\n'; // TODO nextline
756 
757  //if (tree.data.id >= 0)
758  // ostr << "<!-- " << tree.data.id << " /-->\n";
759  }
760  return ostr;
761 }
762 
763 template <>
764 const drain::EnumDict<int,XML>::dict_t drain::EnumDict<int,XML>::dict;
765 
766 
767 template <class E, bool EX, class P>
768 std::ostream & operator<<(std::ostream &ostr, const UnorderedMultiTree<NodeXML<E>,EX,P> & tree){
769  // DOC def? TODO: preamble/prologToStream()
770  NodeXML<E>::docTypeToStream(ostr); // must be member, to support virtual?
771  NodeXML<E>::toStream(ostr, tree, "");
772  return ostr;
773 }
774 
775 
776 // CSS element selector.
777 class SelectorXML : public std::string {
778 
779 public:
780 
781  static
782  const char CLASS = '.';
783 
784  static
785  const char ID = '#';
786 
787 
788  inline
789  SelectorXML(const std::string &s) : std::string(s){
790  }
791 
792  inline
793  SelectorXML(const char *s) : std::string(s){
794  }
795 
796  template <class ...T>
797  inline
798  SelectorXML(T... args) : std::string(StringBuilder<>(args...)){
799  }
800 
801 };
802 
803 // CSS class selector.
807 class SelectorXMLcls : public SelectorXML {
808 public:
809 
810  template <class C>
811  inline
812  SelectorXMLcls(const C &cls) : SelectorXML(CLASS, cls){
813  }
814 
815  template <class E, class C>
816  inline
817  SelectorXMLcls(const E &elem, const C &cls) : SelectorXML(elem, CLASS, cls){
818  }
819 
820 };
821 
822 class SelectorXMLid : public SelectorXML {
823 public:
824 
825  template <class T>
826  inline
827  SelectorXMLid(const T & arg) : SelectorXML(ID, arg){
828  }
829 
830 };
831 
832 
833 /*
834 inline
835 std::ostream & operator<<(std::ostream &ostr, const TreeXML & t){
836  // DOC def? TODO: preamble/prologToStream()
837  TreeXML::node_data_t::docTypeToStream(ostr); // must be member, to support virtual?
838  TreeXML::node_data_t::toStream(ostr, t, "");
839  return ostr;
840 }
841 */
842 
843 
844 } // drain::
845 
846 #endif /* TREEXML_H_ */
847 
Definition: Castable.h:76
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 & deprecating(const TT &... args)
Feature will be removed. Special type of Logger::note().
Definition: Log.h:519
Logger & reject(const TT &... args)
Some input has been rejected, for example by a syntax.
Definition: Log.h:608
Definition: TreeXML.h:341
bool typeIs(const T2 &arg, const T3... args) const
Return true, if type is any of the arguments.
Definition: TreeXML.h:123
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
static std::ostream & toStream(std::ostream &ostr, const V &t, const std::string &defaultTag="", int indent=0)
"Forward definition" of Tree::toOstream
virtual void handleType(const T &type)
Internal function called after setType()
Definition: TreeXML.h:439
std::ostream & nodeToStream(std::ostream &ostr) const
Note: Not designed for XML output, this is more for debugging (in tree dumps),.
Definition: TreeXML.h:518
virtual const std::string & getTag() const
Definition: TreeXML.h:192
std::map< std::string, std::string > xmldoc_attrib_map_t
NOTE: these could/should be templated, in TreeXML<...> right?
Definition: TreeXML.h:450
static const std::string & getTag(const T &type)
Definition: TreeXML.h:206
void set(const std::initializer_list< std::pair< const char *, const Variable > > &args)
Definition: TreeXML.h:268
static std::ostream & docTypeToStream(std::ostream &ostr)
Write the XML definition beginning any XML document.
Definition: TreeXML.h:406
static std::ostream & docToStream(std::ostream &ostr, const V &tree)
Definition: TreeXML.h:733
Definition: Path.h:112
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
void addClass(const TT &... args)
Style class.
Definition: TreeXML.h:220
static X & xmlAssignNode(X &dst, const X &src)
Assign another tree structure to another.
Definition: XML.h:497
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
Definition: DataSelector.cpp:1277
Wrapper for unique (static) dictionary of enum values.
Definition: EnumFlags.h:66
static const std::string name
Default implementation: name returned by std::type_info::name()
Definition: Type.h:558