Loading...
Searching...
No Matches
XML.h
1/*
2
3MIT License
4
5Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25 */
26/*
27Part of Rack development has been done in the BALTRAD projects part-financed
28by the European Union (European Regional Development Fund and European
29Neighbourhood 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_XML
41#define DRAIN_XML
42
43#include <ostream>
44
45#include <drain/Sprinter.h>
46#include <drain/FlexibleVariable.h>
47
48#include "ClassXML.h"
49// #include "UtilsXML.h"
50// #include "Flags.h"
51#include "ReferenceMap.h"
52#include "StyleXML.h"
53
54namespace drain {
55
56
58class XML : protected ReferenceMap2<FlexibleVariable> {
59public:
60
61 typedef int intval_t;
62
63 // TODO:
64 // static const intval_t flag_OPEN = 128;
65 // static const intval_t flag_TEXT = 256;
66 static const intval_t UNDEFINED = 0;
67 static const intval_t COMMENT = 1; // || flag_TEXT
68 static const intval_t CTEXT = 2; // || flag_TEXT
69 static const intval_t SCRIPT = 3; // || flag_EXPLICIT || flag_TEXT
70 static const intval_t STYLE = 4; // || flag_EXPLICIT
71 static const intval_t STYLE_SELECT = 5;
72
73 enum entity_t {
74 AMPERSAND = '&',
75 LESS_THAN = '<',
76 EQUAL_TO = '=',
77 GREATER_THAN = '>',
78 NONBREAKABLE_SPACE = ' ',
79 QUOTE = '"',
80 CURLY_LEFT = '{',
81 CURLY_RIGHT = '}',
82 // ---
83 TAB = '\t',
84 NEWLINE = '\n',
85 };
86
87 /* Variants of entity map */
88
90 static
91 const std::map<char,std::string> & getKeyConversionMap();
92
94 static
95 const std::map<char,std::string> & getAttributeConversionMap();
96
98 static
99 const std::map<char,std::string> & getCTextConversionMap();
100
101
103 // OLD static const std::map<char,std::string> encodingMap;
104
106
107
108
109public:
110
111 inline
112 XML(){};
113
114 inline
115 XML(const XML &){
116
117 };
118
119 template <class T> // "final"
120 void setType(const T &t){ // DANGER, without cast?
121 const intval_t t2 = static_cast<intval_t>(t);
122 if (type != t2){
123 reset();
124 type = t2; // also UNDEFINED ok here
125 handleType(); // NOTE: problems, if copy constructor etc. calls setType on a base class – trying to link future members
126 }
127 // handleType(static_cast<T>(t)); REMOVED 2025/09
128 // in derived classes, eg. drain::image::BaseGDAL
129 // warning: case value ‘...’ not in enumerated type
130 }
131
132 // Consider this later, for user-defined (not enumerated) tag types.
133 // virtual
134 // void setType(const std::string & type);
135
136
137 inline
138 const intval_t & getType() const {
139 return type;
140 };
141
143 inline
144 bool typeIsSet() const {
145 return type != UNDEFINED;
146 };
147
149
152 template <class T2, class ...T3>
153 inline
154 bool typeIs(const T2 & arg, const T3... args) const {
155 if (type == static_cast<intval_t>(arg)){
156 return true;
157 }
158 else {
159 return typeIs(args...);
160 }
161 };
162
163
164 std::string ctext;
165
166 // Could be templated, behind Static?
167 static int nextID;
168
169 inline
170 static int getCount(){
171 return nextID;
172 }
173
174
175 inline
176 bool isUndefined() const {
177 return type == UNDEFINED;
178 }
179
180 inline
181 bool isComment() const {
182 return type == COMMENT;
183 }
184
185 inline
186 bool isCText() const {
187 return type == CTEXT;
188 }
189
190 inline
191 bool isStyle() const {
192 return type == STYLE;
193 }
194
195 inline
196 bool isScript() const {
197 return type == SCRIPT;
198 }
199
201 virtual
202 bool isSingular() const;
203
205 virtual
206 bool isExplicit() const;
207
209
217 void reset();
228
231 inline
232 void setId(){
233 link("id", id);
234 }
235
237 inline
238 void setId(const std::string & s){
239 link("id", id = s);
240 }
241
243 template <char C='\0', typename ...TT>
244 inline
245 void setId(const TT & ...args) {
246 link("id", id = drain::StringBuilder<C>(args...));
247 }
248
250 inline
251 const std::string & getId() const {
252 return id;
253 }
254
255
257
261 template <class ...T>
262 inline
263 void setComment(const T & ...args) {
264 this->clear(); // what if also uncommenting needed?
265 // this->clearClasses();
266 type = COMMENT;
267 setText(args...);
268 }
269
271
276 virtual // redef shows variadic args, below?
277 void setText(const std::string & s);
278
279 template <class ...T>
280 void setText(const T & ...args) {
281 setText(StringBuilder<>(args...).str()); // str() to avoid infinite loop
282 }
283
284 template <class ...T>
285 void setTextSafe(const T & ...args) {
286 std::string dst;
287 StringTools::replace(StringBuilder<>(args...).str(), getCTextConversionMap(), dst); // str() to avoid infinite loop
288 setText(dst);
289 //setText(StringTools::replace(m, StringBuilder<>(args...).str(), ctext)); // str() to avoid infinite loop
290 }
291
292 virtual inline
293 const std::string & getText() const {
294 return ctext;
295 }
296
297 inline
298 const std::string & getUrl(){
299 return url;
300 }
301
302 inline
303 void setUrl(const std::string & s){
304 url = s;
305 // ctext = s;
306 }
307
308 template <class ...T>
309 inline
310 void setName(const T & ...args){
311 setAttribute("data-name", drain::StringBuilder<>(args...).str());
312 }
313
314 inline
315 const FlexibleVariable & getName() const {
316 return (*this)["data-name"];
317 }
318
319
320 // ---------------- Attributes ---------------
321
322 virtual inline // shadows - consider virtual
323 bool empty() const {
324 return map_t::empty();
325 }
326
327 inline
328 const map_t & getAttributes() const {
329 return *this;
330 };
331
332 // Maybe controversial. Helps importing sets of variables.
333 inline
334 map_t & getAttributes(){
335 return *this;
336 };
337
338 // Rename getAttribute?
339 inline
340 const drain::FlexibleVariable & get(const std::string & key) const {
341 return (*this)[key];
342 }
343
344 // Rename getAttribute?
345 inline
346 drain::FlexibleVariable & get(const std::string & key){
347 return (*this)[key];
348 }
349
350
351 // Rename getAttribute?
352 template <class V>
353 inline
354 V get(const std::string & key, const V & defaultValue) const {
355 return map_t::get(key, defaultValue);
356 }
357
358 inline
359 std::string get(const std::string & key, const char * defaultValue) const {
360 return map_t::get(key, defaultValue);
361 }
362
364 // But otherways confusing?
365 virtual inline
366 void setAttribute(const std::string & key, const std::string &value){
367 (*this)[key] = value;
368 }
369
371 // But otherways confusing?
372 virtual inline
373 void setAttribute(const std::string & key, const char *value){
374 (*this)[key] = value; // -> handleString()
375 }
376
378 template <class V>
379 inline
380 void setAttribute(const std::string & key, const V & value){
381 (*this)[key] = value; // -> handleString()
382 }
383
384
385 inline
386 void removeAttribute(const std::string & s){
387 iterator it = this->find(s);
388 if (it != this->end()){
389 this->erase(it);
390 }
391 }
392
393
394
395protected:
396
397 inline
398 bool typeIs() const {
399 return false;
400 };
401
402 intval_t type = XML::UNDEFINED;
403
404 // String, still easily allowing numbers through set("id", ...)
405 std::string id;
406 // Consider either/or
407 std::string url;
408
409 virtual inline
410 void handleType(){}; // = 0;
411
412
413public:
414
415 // ------------------ Style ---------------
416
417protected:
418
419 StyleXML style;
420
421public:
422
423 inline
424 const StyleXML & getStyle() const {
425 return style;
426 }
427
428 inline
429 void setStyle(const StyleXML & s){
430 style.clear();
431 SmartMapTools::setValues(style, s);
432 }
433
434 void setStyle(const std::string & value){
435 drain::Logger mout(__FILE__, __FUNCTION__);
436 if (type == UNDEFINED){
437 mout.reject<LOG_WARNING>("setting style for UNDEFINED elem: ", value);
438 mout.unimplemented<LOG_WARNING>("future option: set type to STYLE_SELECT");
439 }
440 else if (type == STYLE){
441 mout.reject<LOG_WARNING>("not setting style for STYLE elem: ", value); // , *this);
442 }
443 else {
444 SmartMapTools::setValues(style, value, ';', ':', "; \t\n"); // sep, equal, trim (also ';' ?)
445 }
446 }
447
448 inline
449 void setStyle(const char *value){
450 setStyle(std::string(value));
451 }
452
453 inline
454 void setStyle(const std::string & key, const std::string & value){
455 drain::Logger mout(__FILE__, __FUNCTION__);
456 if (type == UNDEFINED){
457 mout.reject<LOG_WARNING>("setting style for UNDEFINED elem: ", key, '=', value);
458 mout.unimplemented<LOG_WARNING>("future option: set type to STYLE_SELECT");
459 }
460 else if (type == STYLE){
461 mout.reject<LOG_WARNING>("not setting style for STYLE elem: ", value); // , *this);
462 }
463 else {
464 this->style[key] = value;
465 }
466 }
467
468
470
476 template <class V>
477 inline
478 void setStyle(const std::string & key, const std::initializer_list<V> &l){
479 // const std::initializer_list<Variable::init_pair_t > &l
480 if (type == STYLE){ // typeIs(STYLE) fails
481 drain::Logger mout(__FILE__, __FUNCTION__);
482 mout.warn("Setting style of STYLE? initializer_list<", drain::TypeName<V>::str(), "> = ", sprinter(l)); // , StyleXML::styleLineLayout ?
483 }
484 this->style[key] = l;
485 }
486
487
489
492 template <class V>
493 inline
494 void setStyle(const std::string & key, const V & value){
495
496 if (type == STYLE){
497 drain::Logger(__FILE__, __FUNCTION__).reject<LOG_WARNING>("Setting style of STYLE: ", key, "=", value);
498 }
499 else {
500 this->style[key] = value;
501 }
502 }
503
504 inline
505 void setStyle(const std::initializer_list<std::pair<const char *,const drain::Variable> > &args){
507 }
508
509
510
511protected:
512
513 // ------------------ Style Class ---------------
514
515 ClassListXML classList;
516
517public:
518
519 const ClassListXML & getClasses() const {
520 return classList;
521 }
522
523 template <typename ... TT>
524 inline
525 void addClass(const TT &... args) {
526 classList.add(args...);
527 }
528
532 template <class V>
533 inline
534 bool hasClass(const V & cls) const {
535 return classList.has(cls);
536 }
537
538 inline
539 void removeClass(const std::string & s) {
540 classList.remove(s);
541 }
542
543 inline
544 void clearClasses(){
545 classList.clear();
546 }
547
548
549 virtual
550 void specificAttributesToStream(std::ostream & ostr) const;
551
552
553 enum tag_display_mode {
554 FLEXIBLE_TAG = 0, // <TAG>...</TAG> or <TAG/>
555 OPENING_TAG= 1, // <TAG>
556 CLOSING_TAG = 2, // </TAG>
557 EMPTY_TAG = OPENING_TAG | CLOSING_TAG, // element has no descendants: <hr/>
558 NON_EMPTY_TAG, // opening and closing tags must appear, even when empty: <script></script>
559 };
560
561
562 virtual
563 std::ostream & nodeToStream(std::ostream & ostr, tag_display_mode mode=EMPTY_TAG) const = 0;
564
565// ----------------- Static utilities for derived classes ----------------------
566
567
568 template <class TR>
569 static
570 std::ostream & toStream(std::ostream & ostr, const TR & tree, const std::string & defaultTag="ELEM", int indent=0);
571
573
575 static
576 const std::map<char,std::string> & getEntityMap();
577
579
583 template <class V>
584 static inline
585 void xmlAttribToStream(std::ostream &ostr, const std::string & key, const V &value){
586 //StringTools::replace(XML::encodingMap, data.ctext, ostr);
587 //ostr << ' ' << key << '=' << '"' << value << '"'; // << ' ';
588
589 static const std::map<char,char> keyMap = {
590 {' ','_'},
591 {'"','_'},
592 {'=','_'},
593 };
594 ostr << ' ';
595 StringTools::replace(key, keyMap, ostr); // XML::encodingMap
596 //StringTools::replace(getEntityMap(), key, ostr); // XML::encodingMap
597
598 static const std::map<char,std::string> valueMap = {
599 {entity_t::QUOTE, "'"},
600 {entity_t::LESS_THAN,"(("},
601 {entity_t::GREATER_THAN,"))"},
602 };
603 ostr << '=' << '"';
604 StringTools::replace(value, valueMap, ostr); // XML::encodingMap
605 //StringTools::replace(getEntityMap(), value, ostr); // XML::encodingMap
606 ostr << '"';
607 //<< key << '=' << '"' << value << '"'; // << ' ';
608 }
609
611
614 template <typename T>
615 static inline
616 T & xmlAssign(T & dst, const T & src){
617
618 if (&src != &dst){
619 dst.clear(); // clears children...
620 // ... but not copying src? (TreeUtils?)
621 // also dst->clear();
622 xmlAssignNode(dst.data, src);
623 /*
624 dst->setType(src->getType());
625 dst->setText(src->ctext); //CTXX
626 dst->getAttributes() = src->getAttributes();
627 */
628 }
629
630 return dst;
631 }
632
634
637 template <typename TX>
638 static inline
639 TX & xmlAssign(TX & dst, const typename TX::xml_node_t & src){
640
641 xmlAssignNode(dst.data, src);
642 /*
643 if (&src != &dst.data){
644 dst->clear(); NOW reset()
645 dst->getAttributes().importMap(src.getAttributes());
646 dst->setStyle(src.getStyle());
647 dst->setText(src.ctext);
648 }
649 */
650 return dst;
651 }
652
654
659 template <typename N>
660 static inline
661 N & xmlAssignNode(N & dst, const N & src){
662
663 if (&src != &dst){
664 //dst.clear(); // clear attributes,
665 //if (!dst.typeIs(src.getNativeType())){
666 if (dst.getType() != src.getType()){
667 dst.reset(); // clear attributes, style, cstring and type.
668 // Warning! Dangerous situation. does not create links
669 dst.setType(src.getType());
670 }
671 // dst.setType(src.getType()); // important: creates links!
672 // dst.handleType(src.getNativeType()); // NEW
673 dst.getAttributes().importMap(src.getAttributes());
674 dst.setStyle(src.getStyle());
675 // dst.setText(src.ctext); // wrong! set type to CTEXT
676 dst.ctext = src.ctext;
677 }
678
679 return dst;
680 }
681
683
686 template <typename T, typename V>
687 static inline
688 T & xmlAssign(T & tree, const V & arg){
689 tree->set(arg);
690 return tree;
691 }
692
694
697 template <typename T>
698 static
699 //T & xmlAssign(T & tree, std::initializer_list<std::pair<const char *,const char *> > l){
700 T & xmlAssign(T & tree, std::initializer_list<std::pair<const char *,const Variable> > l){
701
702 //switch (static_cast<intval_t>(tree->getType())){
703 switch (tree->getType()){
704 case STYLE:
705 for (const auto & entry: l){
706 T & elem = tree[entry.first];
707 elem->setType(STYLE_SELECT);
708 drain::SmartMapTools::setValues(elem->getAttributes(), entry.second, ';', ':', std::string(" \t\n"));
709 }
710 break;
711 case UNDEFINED:
712 tree->setType(STYLE_SELECT);
713 // no break
714 case STYLE_SELECT:
715 default:
716 tree->set(l);
717 break;
718 }
719
720 return tree;
721 };
722
723 // UNDER CONSTRUCTION!
725
730 template <typename TX>
731 static inline // NOT YET as template specification of xmlAssign(...)
732 TX & xmlAssignString(TX & tree, const std::string & s){
733 if (tree->isUndefined()){
734 tree->setType(CTEXT);
735 }
736 tree->ctext = s;
737 return tree;
738 }
739
740 template <typename TX>
741 static inline // NOT YET as template specification of xmlAssign(...)
742 TX & xmlAppendString(TX & tree, const std::string & s){
743 if (tree->isCText()){
744 tree->ctext += s;
745 return tree;
746 }
747 else if (tree->isUndefined()){
748 tree->setType(CTEXT);
749 // tree->setText(s);
750 tree->ctext += s;
751 return tree;
752 }
753 else {
754 // drain::Logger(__FILE__, __FUNCTION__).error("Assign string...");
755 TX & child = tree.addChild();
756 child->setType(CTEXT);
757 child->setText(s);
758 return child;
759 }
760 }
761
763
767 template <typename TX>
768 static inline
769 TX & xmlSetType(TX & tree, const typename TX::node_data_t::xml_tag_t & type){
770 tree->setType(type);
771 return tree;
772 }
773
774
780 template <typename T>
781 static
782 T & xmlAddChild(T & tree, const std::string & key){
783 typename T::node_data_t::xml_tag_t type = xmlRetrieveDefaultType(tree.data);
784
785 if (!key.empty()){
786 return tree[key](type);
787 }
788 else {
789 std::stringstream k; // ("elem");
790 k << "elem"; // number with 4 digits overwrites this?
791 k.width(3); // consider static member prefix
792 k.fill('0');
793 k << tree.getChildren().size();
794 return tree[k.str()](type);
795 }
796 }
797
798 template <typename N>
799 static
800 typename N::xml_tag_t xmlRetrieveDefaultType(const N & parentNode){
801 typedef typename N::xml_default_elem_map_t map_t;
802 const typename map_t::const_iterator it = N::xml_default_elems.find(parentNode.getNativeType());
803 if (it != N::xml_default_elems.end()){
804 return (it->second);
805 }
806 else {
807 return static_cast<typename N::xml_tag_t>(0);
808 }
809 }
810
811 /*
812 template <typename T>
813 static
814 T & xmlGuessType(const typename T::node_data_t & parentNode, T & child){
815 typedef typename T::node_data_t::xml_default_elem_map_t map_t;
816 const typename map_t::const_iterator it = T::node_data_t::xml_default_elems.find(parentNode.getNativeType());
817 if (it != T::node_data_t::xml_default_elems.end()){
818 child->setType(it->second);
819 drain::Logger(__FILE__, __FUNCTION__).experimental<LOG_WARNING>("Default type set: ", child->getTag());
820 }
821 return child;
822 }
823 */
824
825
826
827};
828
829
830template <>
832
833DRAIN_ENUM_OSTREAM(XML::entity_t);
834
835template <class TR>
836std::ostream & XML::toStream(std::ostream & ostr, const TR & tree, const std::string & defaultTag, int indent){
837
838 drain::Logger mout(__FILE__,__FUNCTION__);
839
840 const typename TR::container_t & children = tree.getChildren();
841
842 // const XML & data = tree.data; // template type forcing, keep here for programming aid.
843 const typename TR::node_data_t & data = tree.data; // template used
844
845 tag_display_mode mode = EMPTY_TAG;
846
847 if (data.isCText()){ // this can be true only at root, and rarely so...? (because recursive call not reached, if ctext)
848 data.nodeToStream(ostr, mode);
849 // ostr << "<!--TX-->";
850 return ostr;
851 }
852
853 if (!data.ctext.empty()){
854 // mout.warn("Non-CTEXT-elem with ctext: <", data.getTag(), " id='", data.getId(), "' ...>, text='", data.ctext, "'");
855 if (data.isSingular()){
856 // mout.warn("Skipping CTEXT of a singular element <", tree->getTag(), " id='", data.getId(), "' ...> , CTEXT: '", data.ctext, "'");
857 mode = EMPTY_TAG;
858 }
859 else {
860 mode = OPENING_TAG;
861 }
862 }
863
864 if (!children.empty()){
865 mode = OPENING_TAG;
866 if (data.isSingular()){
867 mout.warn("Singular (hence normally empty) element <", tree->getTag(), " id='", data.getId(), "' ...> has ", children.size(), " children?");
868 }
869 }
870
871 // mout.attention("Hey! ", TypeName<TR>::str(), " with ", TypeName<typename TR::node_data_t>::str(), "Explicit ", data.isExplicit(), " or implicit ", data.isSingular());
872 if (data.isExplicit()){ // explicit
873 mode = OPENING_TAG;
874 }
875 else if (data.isSingular()){ // <br/> <hr/>
876 mode = EMPTY_TAG;
877 }
878 // Hence, is flexible, "bimodal", supports empty and open-close mode.
879
880 // Indent
881 // std::fill_n(std::ostream_iterator<char>(ostr), 2*indent, ' ');
882 std::string fill(2*indent, ' ');
883 ostr << fill;
884 data.nodeToStream(ostr, mode);
885
886 if (mode == EMPTY_TAG){
887 //ostr << "<!--ET-->";
888 ostr << '\n';
889 return ostr;
890 }
891 else if (data.isStyle()){
892 // https://www.w3.org/TR/xml/#sec-cdata-sect
893 // ostr << "<![CDATA[ \n";
894 // ostr << "<!-- STYLE -->"; WRONG!
895
896 if (!data.ctext.empty()){
897 // TODO: indent
898 ostr << fill << data.ctext;
899 StyleXML::commentToStream(ostr, " TEXT ");
900 ostr << '\n';
901 }
902
903 if (!data.getAttributes().empty()){
904 mout.warn("STYLE elem ", data.getId()," contains attributes, probably meant as style: ", sprinter(data.getAttributes()));
905 ostr << "\n\t /" << "* <!-- DISCARDED attribs ";
906 Sprinter::toStream(ostr, data.getAttributes()); //, StyleXML::styleRecordLayout
907 ostr << " /--> *" << "/" << '\n';
908 }
909
910 if (!data.style.empty()){
911 ostr << fill;
912 StyleXML::commentToStream(ostr, "STYLE OBJ");
913 ostr << '\n';
914 for (const auto & attr: data.style){
915 ostr << fill << " ";
916 Sprinter::pairToStream(ostr, attr, StyleXML::styleRecordLayout); // {" :;"}
917 //attr.first << ':' attr.first << ':';
918 ostr << '\n';
919 }
920 // ostr << fill << "}\n";
921 // Sprinter::sequenceToStream(ostr, entry.second->getAttributes(), StyleXML::styleRecordLayoutActual);
922 // ostr << '\n';
923 }
924 ostr << '\n';
925
926 ostr << fill;
927 StyleXML::commentToStream(ostr, "style ELEMS");
928 ostr << '\n';
929
930 for (const auto & entry: tree.getChildren()){
931
932 if (entry.second->isComment()){
933 // StringTools::replace();
934 ostr << fill << "/* "<< entry.second->ctext << " */" << '\n';
935 // XML::toStream(ostr, entry.second, defaultTag, indent); // indent not used?
936 continue;
937 }
938
939 if (!entry.second->ctext.empty()){
940 //ostr << fill << "<!-- elem("<< entry.first << ") ctext /-->" << '\n';
941 ostr << fill << " " << entry.first << " {" << entry.second->ctext << "} /* CTEXT */ \n";
942 }
943
944 if (!entry.second->getAttributes().empty()){
945 ostr << fill << " " << entry.first << " {\n";
946 for (const auto & attr: entry.second->getAttributes()){
947 ostr << fill << " ";
948 ostr << attr.first << ':' << attr.second << ';';
949 ostr << '\n';
950 }
951 ostr << fill << " }\n";
952 ostr << '\n';
953 }
954
955 }
956 ostr << "\n"; // end CTEXT
957 // ostr << " ]]>\n"; // end CTEXT
958 // end STYLE defs
959 ostr << fill;
960
961 }
962 else {
963
964 // Elements "own" CTEXT will be always output first -> check for problems, if other elements added first.
965 // ostr << data.ctext;
966
967 if (data.isScript()){
968 ostr << data.ctext; // let < and > pass through
969 }
970 else {
971 ostr << data.ctext; // let < and > pass through
972 // StringTools::replace(XML::encodingMap, data.ctext, ostr); // sometimes an issue? // any time issue?
973 }
974
975 // Detect if all the children are of type CTEXT, to be rendered in a single line.
976 // Note: potential re-parsing will probably detect them as a single CTEXT element.
977 bool ALL_CTEXT = true; // (!data.ctext.empty()) || !children.empty();
978
979 for (const auto & entry: children){
980 if (!entry.second->isCText()){
981 ALL_CTEXT = false;
982 break;
983 }
984 }
985
986 // ALL_CTEXT = false;
987
988 if (ALL_CTEXT){
989 // ostr << "<!--ALL_CTEXT-->";
990 char sep=0;
991 for (const auto & entry: children){
992 if (sep){
993 ostr << sep;
994 }
995 else {
996 sep = ' '; // consider global setting?
997 }
998 //ostr << entry.second->getText();
999 //StringTools::replace(XML::encodingMap, entry.second->getText(), ostr); // any time issue?
1000 StringTools::replace(entry.second->getText(), getEntityMap(), ostr); // any time issue?
1001 // ostr << entry.second->getText();
1002 }
1003 }
1004 else {
1005 // ostr << "<!-- RECURSION -->";
1006 ostr << '\n';
1008 for (const auto & entry: children){
1009 toStream(ostr, entry.second, entry.first, indent+1); // Notice, no ++indent
1010 // "implicit" newline
1011 }
1012 ostr << fill; // for CLOSING tag
1013 }
1014
1015 }
1016
1017 // ostr << "<!-- END "<< data.getId() << ' ' << data.getTag() << '(' << data.getType() << ')' << "-->";
1018
1019 data.nodeToStream(ostr, CLOSING_TAG);
1020 ostr << '\n'; // Always after closing tag!
1021
1022 return ostr;
1023}
1024
1025
1026
1027} // drain::
1028
1029#endif /* DRAIN_XML */
1030
Two-way mapping between strings and objects of template class T.
Definition Dictionary.h:63
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition Log.h:312
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition Log.h:430
Logger & reject(const TT &... args)
Some input has been rejected, for example by a syntax.
Definition Log.h:610
A map of references to base type scalars, arrays or std::string; changing values in either are equiva...
Definition ReferenceMap.h:69
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
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 void replace(const std::string &src, char search, char repl, std::ostream &ostr)
Replaces instances of 'search' to 'repl' in src.
Definition String.cpp:94
static void commentToStream(std::ostream &ostr, const T &v)
Practical utility, helps in adding C++ code commenting...
Definition StyleXML.h:69
VariableT is a final class applied through typedefs Variable, Reference and FlexibleVariable.
Definition VariableT.h:87
Base class for XML "nodes", to be data elements T for drain::Tree<T>
Definition TreeXML.h:80
static const std::map< char, std::string > & getEntityMap()
Handy map for converting characters to XML entities. Example: '&' -> "&".
Definition XML.cpp:186
bool typeIs(const T2 &arg, const T3... args) const
Return true, if type is any of the arguments.
Definition XML.h:154
virtual bool isExplicit() const
Tell if this element should always have an explicit closing tag even when empty, like <STYLE></STYLE>
Definition XML.cpp:147
bool typeIsSet() const
Return true, if type is any of the arguments.
Definition XML.h:144
static const std::map< char, std::string > & getKeyConversionMap()
Characters that must be avoided in XML attribute keys: space, tab, =.
Definition XML.cpp:82
virtual bool isSingular() const
Tell if this element should always have an explicit closing tag even when empty, like <STYLE></STYLE>
Definition XML.cpp:141
void addClass(const TT &... args)
Style class.
Definition TreeXML.h:220
static T & xmlAssign(T &dst, const T &src)
Assign another tree structure to another.
Definition XML.h:616
void setId()
Makes ID a visible attribute.
Definition XML.h:232
void setStyle(const std::string &key, const std::initializer_list< V > &l)
Set style of an element.
Definition XML.h:478
const std::string & getId() const
Returns ID of this element. Hopefully a unique ID...
Definition XML.h:251
virtual void setAttribute(const std::string &key, const char *value)
Default implementation. Needed for handling units in strings, like "50%" or "640px".
Definition XML.h:373
static TX & xmlAssignString(TX &tree, const std::string &s)
When assigning a string, create new element unless the element itself is of type CTEXT.
Definition XML.h:732
static const std::map< char, std::string > & getAttributeConversionMap()
Characters that must be avoided in XML attribute values: ".
Definition XML.cpp:97
static const std::map< char, std::string > & getCTextConversionMap()
Characters to avoid in XML free text: <, >, {, },.
Definition XML.cpp:109
static void xmlAttribToStream(std::ostream &ostr, const std::string &key, const V &value)
Handy map for converting characters to XML entities. Example: '&' -> "&".
Definition XML.h:585
void setId(const TT &...args)
Concatenates arguments to an id.
Definition XML.h:245
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
static TX & xmlSetType(TX &tree, const typename TX::node_data_t::xml_tag_t &type)
Definition XML.h:769
static std::ostream & toStream(std::ostream &ostr, const TR &tree, const std::string &defaultTag="ELEM", int indent=0)
Definition XML.h:836
static T & xmlAddChild(T &tree, const std::string &key)
Definition XML.h:782
static N & xmlAssignNode(N &dst, const N &src)
Assign tree node (data) to another.
Definition XML.h:661
static TX & xmlAssign(TX &dst, const typename TX::xml_node_t &src)
Copy node data to tree.
Definition XML.h:639
void reset()
Keep the element type but clear style, class and string data.
Definition XML.cpp:134
ReferenceMap2< FlexibleVariable > map_t
User may optionally filter attributes and CTEST with this using StringTools::replace(XML::encodingMap...
Definition XML.h:105
void setStyle(const std::string &key, const V &value)
For element/class/id, assign ...
Definition XML.h:494
virtual void setText(const std::string &s)
Assign the text content of this node. If the node type is undefined, set it to CTEXT.
Definition XML.cpp:152
static T & xmlAssign(T &tree, std::initializer_list< std::pair< const char *, const Variable > > l)
Tree.
Definition XML.h:700
void setComment(const T &...args)
Make this node a comment. Contained tree will not be deleted. In current version, attributes WILL be ...
Definition XML.h:263
void setAttribute(const std::string &key, const V &value)
"Final" implementation.
Definition XML.h:380
static T & xmlAssign(T &tree, const V &arg)
Assign property to a XML tree node.
Definition XML.h:688
bool hasClass(const V &cls) const
Definition XML.h:534
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 XML.h:238
Definition DataSelector.cpp:1277
VariableT< VariableInitializer< ReferenceT< VariableBase > > > FlexibleVariable
Value container supporting dynamic type with own memory and optional linking (referencing) of externa...
Definition FlexibleVariable.h:67
A container for a static dictionary of enumeration values.
Definition EnumFlags.h:69
Default implementation.
Definition Type.h:541