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
163protected:
164
165 inline
166 bool typeIs() const {
167 return false;
168 };
169
170 intval_t type = XML::UNDEFINED;
171
172 // String, still easily allowing numbers through set("id", ...)
173 std::string id;
174 // Consider either/or
175 std::string url;
176
177 virtual inline
178 void handleType(){}; // = 0;
179
180
181public:
182
183 std::string ctext;
184
185 // Could be templated, behind Static?
186 static int nextID;
187
188 inline
189 static int getCount(){
190 return nextID;
191 }
192
193
194 inline
195 bool isUndefined() const {
196 return type == UNDEFINED;
197 }
198
199 inline
200 bool isComment() const {
201 return type == COMMENT;
202 }
203
204 inline
205 bool isCText() const {
206 return type == CTEXT;
207 }
208
209 inline
210 bool isStyle() const {
211 return type == STYLE;
212 }
213
214 inline
215 bool isScript() const {
216 return type == SCRIPT;
217 }
218
220 virtual
221 bool isSingular() const;
222
224 virtual
225 bool isExplicit() const;
226
228
236 void reset();
247
250 inline
251 void setId(){
252 link("id", id);
253 }
254
256 inline
257 void setId(const std::string & s){
258 link("id", id = s);
259 }
260
262 template <char C='\0', typename ...TT>
263 inline
264 void setId(const TT & ...args) {
265 link("id", id = drain::StringBuilder<C>(args...));
266 }
267
269 inline
270 const std::string & getId() const {
271 return id;
272 }
273
274
276
280 template <class ...T>
281 inline
282 void setComment(const T & ...args) {
283 this->clear(); // what if also uncommenting needed?
284 // this->clearClasses();
285 type = COMMENT;
286 setText(args...);
287 }
288
290
295 virtual // redef shows variadic args, below?
296 void setText(const std::string & s);
297
298 template <class ...T>
299 void setText(const T & ...args) {
300 setText(StringBuilder<>(args...).str()); // str() to avoid infinite loop
301 }
302
303 template <class ...T>
304 void setTextSafe(const T & ...args) {
305 std::string dst;
306 StringTools::replace(getCTextConversionMap(), StringBuilder<>(args...).str(), dst); // str() to avoid infinite loop
307 setText(dst);
308 //setText(StringTools::replace(m, StringBuilder<>(args...).str(), ctext)); // str() to avoid infinite loop
309 }
310
311 virtual inline
312 const std::string & getText() const {
313 return ctext;
314 }
315
316 inline
317 const std::string & getUrl(){
318 return url;
319 }
320
321 inline
322 void setUrl(const std::string & s){
323 url = s;
324 // ctext = s;
325 }
326
327 template <class ...T>
328 inline
329 void setName(const T & ...args){
330 setAttribute("data-name", drain::StringBuilder<>(args...));
331 }
332
333
334 // ---------------- Attributes ---------------
335
336 virtual inline // shadows - consider virtual
337 bool empty() const {
338 return map_t::empty();
339 }
340
341 inline
342 const map_t & getAttributes() const {
343 return *this;
344 };
345
346 // Maybe controversial. Helps importing sets of variables.
347 inline
348 map_t & getAttributes(){
349 return *this;
350 };
351
352 // Rename getAttribute?
353 inline
354 const drain::FlexibleVariable & get(const std::string & key) const {
355 return (*this)[key];
356 }
357
358 // Rename getAttribute?
359 inline
360 drain::FlexibleVariable & get(const std::string & key){
361 return (*this)[key];
362 }
363
364
365 // Rename getAttribute?
366 template <class V>
367 inline
368 V get(const std::string & key, const V & defaultValue) const {
369 return map_t::get(key, defaultValue);
370 }
371
372 inline
373 std::string get(const std::string & key, const char * defaultValue) const {
374 return map_t::get(key, defaultValue);
375 }
376
378 // But otherways confusing?
379 virtual inline
380 void setAttribute(const std::string & key, const std::string &value){
381 (*this)[key] = value;
382 }
383
385 // But otherways confusing?
386 virtual inline
387 void setAttribute(const std::string & key, const char *value){
388 (*this)[key] = value; // -> handleString()
389 }
390
392 template <class V>
393 inline
394 void setAttribute(const std::string & key, const V & value){
395 (*this)[key] = value; // -> handleString()
396 }
397
398
399 inline
400 void removeAttribute(const std::string & s){
401 iterator it = this->find(s);
402 if (it != this->end()){
403 this->erase(it);
404 }
405 }
406
407
408 // ------------------ Style ---------------
409
410protected:
411
412 StyleXML style;
413
414public:
415
416 inline
417 const StyleXML & getStyle() const {
418 return style;
419 }
420
421 inline
422 void setStyle(const StyleXML & s){
423 style.clear();
424 SmartMapTools::setValues(style, s);
425 }
426
427 void setStyle(const std::string & value){
428 drain::Logger mout(__FILE__, __FUNCTION__);
429 if (type == UNDEFINED){
430 mout.reject<LOG_WARNING>("setting style for UNDEFINED elem: ", value);
431 mout.unimplemented<LOG_WARNING>("future option: set type to STYLE_SELECT");
432 }
433 else if (type == STYLE){
434 mout.reject<LOG_WARNING>("not setting style for STYLE elem: ", value); // , *this);
435 }
436 else {
437 SmartMapTools::setValues(style, value, ';', ':', "; \t\n"); // sep, equal, trim (also ';' ?)
438 }
439 }
440
441 inline
442 void setStyle(const char *value){
443 setStyle(std::string(value));
444 }
445
446 inline
447 void setStyle(const std::string & key, const std::string & value){
448 drain::Logger mout(__FILE__, __FUNCTION__);
449 if (type == UNDEFINED){
450 mout.reject<LOG_WARNING>("setting style for UNDEFINED elem: ", key, '=', value);
451 mout.unimplemented<LOG_WARNING>("future option: set type to STYLE_SELECT");
452 }
453 else if (type == STYLE){
454 mout.reject<LOG_WARNING>("not setting style for STYLE elem: ", value); // , *this);
455 }
456 else {
457 this->style[key] = value;
458 }
459 }
460
461
463
469 template <class V>
470 inline
471 void setStyle(const std::string & key, const std::initializer_list<V> &l){
472 // const std::initializer_list<Variable::init_pair_t > &l
473 if (type == STYLE){ // typeIs(STYLE) fails
474 drain::Logger mout(__FILE__, __FUNCTION__);
475 mout.warn("Setting style of STYLE? initializer_list<", drain::TypeName<V>::str(), "> = ", sprinter(l)); // , StyleXML::styleLineLayout ?
476 }
477 this->style[key] = l;
478 }
479
480
482
485 template <class V>
486 inline
487 void setStyle(const std::string & key, const V & value){
488
489 if (type == STYLE){
490 drain::Logger(__FILE__, __FUNCTION__).reject<LOG_WARNING>("Setting style of STYLE: ", key, "=", value);
491 }
492 else {
493 this->style[key] = value;
494 }
495 }
496
497 inline
498 void setStyle(const std::initializer_list<std::pair<const char *,const drain::Variable> > &args){
500 }
501
502
503
504protected:
505
506 // ------------------ Style Class ---------------
507
508 ClassListXML classList;
509
510public:
511
512 const ClassListXML & getClasses() const {
513 return classList;
514 }
515
516 template <typename ... TT>
517 inline
518 void addClass(const TT &... args) {
519 classList.add(args...);
520 }
521
525 template <class V>
526 inline
527 bool hasClass(const V & cls) const {
528 return classList.has(cls);
529 }
530
531 inline
532 void removeClass(const std::string & s) {
533 classList.remove(s);
534 }
535
536 inline
537 void clearClasses(){
538 classList.clear();
539 }
540
541
542 virtual
543 void specificAttributesToStream(std::ostream & ostr) const;
544
545
546 enum tag_display_mode {
547 FLEXIBLE_TAG = 0, // <TAG>...</TAG> or <TAG/>
548 OPENING_TAG= 1, // <TAG>
549 CLOSING_TAG = 2, // </TAG>
550 EMPTY_TAG = OPENING_TAG | CLOSING_TAG, // element has no descendants: <hr/>
551 NON_EMPTY_TAG, // opening and closing tags must appear, even when empty: <script></script>
552 };
553
554
555 virtual
556 std::ostream & nodeToStream(std::ostream & ostr, tag_display_mode mode=EMPTY_TAG) const = 0;
557
558// ----------------- Static utilities for derived classes ----------------------
559
560
561 template <class TR>
562 static
563 std::ostream & toStream(std::ostream & ostr, const TR & tree, const std::string & defaultTag="ELEM", int indent=0);
564
566
568 static
569 const std::map<char,std::string> & getEntityMap();
570
572
576 template <class V>
577 static inline
578 void xmlAttribToStream(std::ostream &ostr, const std::string & key, const V &value){
579 //StringTools::replace(XML::encodingMap, data.ctext, ostr);
580 //ostr << ' ' << key << '=' << '"' << value << '"'; // << ' ';
581
582 static const std::map<char,char> keyMap = {
583 {' ','_'},
584 {'"','_'},
585 {'=','_'},
586 };
587 ostr << ' ';
588 StringTools::replace(keyMap, key, ostr); // XML::encodingMap
589 //StringTools::replace(getEntityMap(), key, ostr); // XML::encodingMap
590
591 static const std::map<char,std::string> valueMap = {
592 {entity_t::QUOTE, "'"},
593 {entity_t::LESS_THAN,"(("},
594 {entity_t::GREATER_THAN,"))"},
595 };
596 ostr << '=' << '"';
597 StringTools::replace(valueMap, value, ostr); // XML::encodingMap
598 //StringTools::replace(getEntityMap(), value, ostr); // XML::encodingMap
599 ostr << '"';
600 //<< key << '=' << '"' << value << '"'; // << ' ';
601 }
602
604
607 template <typename T>
608 static inline
609 T & xmlAssign(T & dst, const T & src){
610
611 if (&src != &dst){
612 dst.clear(); // clears children...
613 // ... but not copying src? (TreeUtils?)
614 // also dst->clear();
615 xmlAssignNode(dst.data, src);
616 /*
617 dst->setType(src->getType());
618 dst->setText(src->ctext); //CTXX
619 dst->getAttributes() = src->getAttributes();
620 */
621 }
622
623 return dst;
624 }
625
627
630 template <typename TX>
631 static inline
632 TX & xmlAssign(TX & dst, const typename TX::xml_node_t & src){
633
634 xmlAssignNode(dst.data, src);
635 /*
636 if (&src != &dst.data){
637 dst->clear(); NOW reset()
638 dst->getAttributes().importMap(src.getAttributes());
639 dst->setStyle(src.getStyle());
640 dst->setText(src.ctext);
641 }
642 */
643 return dst;
644 }
645
647
652 template <typename N>
653 static inline
654 N & xmlAssignNode(N & dst, const N & src){
655
656 if (&src != &dst){
657 //dst.clear(); // clear attributes,
658 //if (!dst.typeIs(src.getNativeType())){
659 if (dst.getType() != src.getType()){
660 dst.reset(); // clear attributes, style, cstring and type.
661 // Warning! Dangerous situation. does not create links
662 dst.setType(src.getType());
663 }
664 // dst.setType(src.getType()); // important: creates links!
665 // dst.handleType(src.getNativeType()); // NEW
666 dst.getAttributes().importMap(src.getAttributes());
667 dst.setStyle(src.getStyle());
668 // dst.setText(src.ctext); // wrong! set type to CTEXT
669 dst.ctext = src.ctext;
670 }
671
672 return dst;
673 }
674
676
679 template <typename T, typename V>
680 static inline
681 T & xmlAssign(T & tree, const V & arg){
682 tree->set(arg);
683 return tree;
684 }
685
687
690 template <typename T>
691 static
692 //T & xmlAssign(T & tree, std::initializer_list<std::pair<const char *,const char *> > l){
693 T & xmlAssign(T & tree, std::initializer_list<std::pair<const char *,const Variable> > l){
694
695 //switch (static_cast<intval_t>(tree->getType())){
696 switch (tree->getType()){
697 case STYLE:
698 for (const auto & entry: l){
699 T & elem = tree[entry.first];
700 elem->setType(STYLE_SELECT);
701 drain::SmartMapTools::setValues(elem->getAttributes(), entry.second, ';', ':', std::string(" \t\n"));
702 }
703 break;
704 case UNDEFINED:
705 tree->setType(STYLE_SELECT);
706 // no break
707 case STYLE_SELECT:
708 default:
709 tree->set(l);
710 break;
711 }
712
713 return tree;
714 };
715
716 // UNDER CONSTRUCTION!
718
723 template <typename TX>
724 static inline // NOT YET as template specification of xmlAssign(...)
725 TX & xmlAssignString(TX & tree, const std::string & s){
726 if (tree->isUndefined()){
727 tree->setType(CTEXT);
728 }
729 tree->ctext = s;
730 return tree;
731 }
732
733 template <typename TX>
734 static inline // NOT YET as template specification of xmlAssign(...)
735 TX & xmlAppendString(TX & tree, const std::string & s){
736 if (tree->isCText()){
737 tree->ctext += s;
738 return tree;
739 }
740 else if (tree->isUndefined()){
741 tree->setType(CTEXT);
742 // tree->setText(s);
743 tree->ctext += s;
744 return tree;
745 }
746 else {
747 // drain::Logger(__FILE__, __FUNCTION__).error("Assign string...");
748 TX & child = tree.addChild();
749 child->setType(CTEXT);
750 child->setText(s);
751 return child;
752 }
753 }
754
756
760 template <typename TX>
761 static inline
762 TX & xmlSetType(TX & tree, const typename TX::node_data_t::xml_tag_t & type){
763 tree->setType(type);
764 return tree;
765 }
766
767
773 template <typename T>
774 static
775 T & xmlAddChild(T & tree, const std::string & key){
776 typename T::node_data_t::xml_tag_t type = xmlRetrieveDefaultType(tree.data);
777
778 if (!key.empty()){
779 return tree[key](type);
780 }
781 else {
782 std::stringstream k; // ("elem");
783 k << "elem"; // number with 4 digits overwrites this?
784 k.width(3); // consider static member prefix
785 k.fill('0');
786 k << tree.getChildren().size();
787 return tree[k.str()](type);
788 }
789 }
790
791 template <typename N>
792 static
793 typename N::xml_tag_t xmlRetrieveDefaultType(const N & parentNode){
794 typedef typename N::xml_default_elem_map_t map_t;
795 const typename map_t::const_iterator it = N::xml_default_elems.find(parentNode.getNativeType());
796 if (it != N::xml_default_elems.end()){
797 return (it->second);
798 }
799 else {
800 return static_cast<typename N::xml_tag_t>(0);
801 }
802 }
803
804 /*
805 template <typename T>
806 static
807 T & xmlGuessType(const typename T::node_data_t & parentNode, T & child){
808 typedef typename T::node_data_t::xml_default_elem_map_t map_t;
809 const typename map_t::const_iterator it = T::node_data_t::xml_default_elems.find(parentNode.getNativeType());
810 if (it != T::node_data_t::xml_default_elems.end()){
811 child->setType(it->second);
812 drain::Logger(__FILE__, __FUNCTION__).experimental<LOG_WARNING>("Default type set: ", child->getTag());
813 }
814 return child;
815 }
816 */
817
818
819
820};
821
822
823template <>
825
826DRAIN_ENUM_OSTREAM(XML::entity_t);
827
828template <class TR>
829std::ostream & XML::toStream(std::ostream & ostr, const TR & tree, const std::string & defaultTag, int indent){
830
831 drain::Logger mout(__FILE__,__FUNCTION__);
832
833 const typename TR::container_t & children = tree.getChildren();
834
835 // const XML & data = tree.data; // template type forcing, keep here for programming aid.
836 const typename TR::node_data_t & data = tree.data; // template used
837
838 tag_display_mode mode = EMPTY_TAG;
839
840 if (data.isCText()){ // this can be true only at root, and rarely so...? (because recursive call not reached, if ctext)
841 data.nodeToStream(ostr, mode);
842 // ostr << "<!--TX-->";
843 return ostr;
844 }
845
846 if (!data.ctext.empty()){
847 // mout.warn("Non-CTEXT-elem with ctext: <", data.getTag(), " id='", data.getId(), "' ...>, text='", data.ctext, "'");
848 if (data.isSingular()){
849 // mout.warn("Skipping CTEXT of a singular element <", tree->getTag(), " id='", data.getId(), "' ...> , CTEXT: '", data.ctext, "'");
850 mode = EMPTY_TAG;
851 }
852 else {
853 mode = OPENING_TAG;
854 }
855 }
856
857 if (!children.empty()){
858 mode = OPENING_TAG;
859 if (data.isSingular()){
860 mout.warn("Singular (hence normally empty) element <", tree->getTag(), " id='", data.getId(), "' ...> has ", children.size(), " children?");
861 }
862 }
863
864 // mout.attention("Hey! ", TypeName<TR>::str(), " with ", TypeName<typename TR::node_data_t>::str(), "Explicit ", data.isExplicit(), " or implicit ", data.isSingular());
865 if (data.isExplicit()){ // explicit
866 mode = OPENING_TAG;
867 }
868 else if (data.isSingular()){ // <br/> <hr/>
869 mode = EMPTY_TAG;
870 }
871 // Hence, is flexible, "bimodal", supports empty and open-close mode.
872
873 // Indent
874 // std::fill_n(std::ostream_iterator<char>(ostr), 2*indent, ' ');
875 std::string fill(2*indent, ' ');
876 ostr << fill;
877 data.nodeToStream(ostr, mode);
878
879 if (mode == EMPTY_TAG){
880 //ostr << "<!--ET-->";
881 ostr << '\n';
882 return ostr;
883 }
884 else if (data.isStyle()){
885 // https://www.w3.org/TR/xml/#sec-cdata-sect
886 // ostr << "<![CDATA[ \n";
887 // ostr << "<!-- STYLE -->"; WRONG!
888
889 if (!data.ctext.empty()){
890 // TODO: indent
891 ostr << fill << data.ctext;
892 StyleXML::commentToStream(ostr, " TEXT ");
893 ostr << '\n';
894 }
895
896 if (!data.getAttributes().empty()){
897 mout.warn("STYLE elem ", data.getId()," contains attributes, probably meant as style: ", sprinter(data.getAttributes()));
898 ostr << "\n\t /" << "* <!-- DISCARDED attribs ";
899 Sprinter::toStream(ostr, data.getAttributes()); //, StyleXML::styleRecordLayout
900 ostr << " /--> *" << "/" << '\n';
901 }
902
903 if (!data.style.empty()){
904 ostr << fill;
905 StyleXML::commentToStream(ostr, "STYLE OBJ");
906 ostr << '\n';
907 for (const auto & attr: data.style){
908 ostr << fill << " ";
909 Sprinter::pairToStream(ostr, attr, StyleXML::styleRecordLayout); // {" :;"}
910 //attr.first << ':' attr.first << ':';
911 ostr << '\n';
912 }
913 // ostr << fill << "}\n";
914 // Sprinter::sequenceToStream(ostr, entry.second->getAttributes(), StyleXML::styleRecordLayoutActual);
915 // ostr << '\n';
916 }
917 ostr << '\n';
918
919 ostr << fill;
920 StyleXML::commentToStream(ostr, "style ELEMS");
921 ostr << '\n';
922
923 for (const auto & entry: tree.getChildren()){
924
925 if (entry.second->isComment()){
926 // StringTools::replace();
927 ostr << fill << "/* "<< entry.second->ctext << " */" << '\n';
928 // XML::toStream(ostr, entry.second, defaultTag, indent); // indent not used?
929 continue;
930 }
931
932 if (!entry.second->ctext.empty()){
933 //ostr << fill << "<!-- elem("<< entry.first << ") ctext /-->" << '\n';
934 ostr << fill << " " << entry.first << " {" << entry.second->ctext << "} /* CTEXT */ \n";
935 }
936
937 if (!entry.second->getAttributes().empty()){
938 ostr << fill << " " << entry.first << " {\n";
939 for (const auto & attr: entry.second->getAttributes()){
940 ostr << fill << " ";
941 ostr << attr.first << ':' << attr.second << ';';
942 ostr << '\n';
943 }
944 ostr << fill << " }\n";
945 ostr << '\n';
946 }
947
948 }
949 ostr << "\n"; // end CTEXT
950 // ostr << " ]]>\n"; // end CTEXT
951 // end STYLE defs
952 ostr << fill;
953
954 }
955 else {
956
957 // Elements "own" CTEXT will be always output first -> check for problems, if other elements added first.
958 // ostr << data.ctext;
959
960 if (data.isScript()){
961 ostr << data.ctext; // let < and > pass through
962 }
963 else {
964 ostr << data.ctext; // let < and > pass through
965 // StringTools::replace(XML::encodingMap, data.ctext, ostr); // sometimes an issue? // any time issue?
966 }
967
968 // Detect if all the children are of type CTEXT, to be rendered in a single line.
969 // Note: potential re-parsing will probably detect them as a single CTEXT element.
970 bool ALL_CTEXT = true; // (!data.ctext.empty()) || !children.empty();
971
972 for (const auto & entry: children){
973 if (!entry.second->isCText()){
974 ALL_CTEXT = false;
975 break;
976 }
977 }
978
979 // ALL_CTEXT = false;
980
981 if (ALL_CTEXT){
982 // ostr << "<!--ALL_CTEXT-->";
983 char sep=0;
984 for (const auto & entry: children){
985 if (sep){
986 ostr << sep;
987 }
988 else {
989 sep = ' '; // consider global setting?
990 }
991 //ostr << entry.second->getText();
992 //StringTools::replace(XML::encodingMap, entry.second->getText(), ostr); // any time issue?
993 StringTools::replace(getEntityMap(), entry.second->getText(), ostr); // any time issue?
994 // ostr << entry.second->getText();
995 }
996 }
997 else {
998 // ostr << "<!-- RECURSION -->";
999 ostr << '\n';
1001 for (const auto & entry: children){
1002 toStream(ostr, entry.second, entry.first, indent+1); // Notice, no ++indent
1003 // "implicit" newline
1004 }
1005 ostr << fill; // for CLOSING tag
1006 }
1007
1008 }
1009
1010 // ostr << "<!-- END "<< data.getId() << ' ' << data.getTag() << '(' << data.getType() << ')' << "-->";
1011
1012 data.nodeToStream(ostr, CLOSING_TAG);
1013 ostr << '\n'; // Always after closing tag!
1014
1015 return ostr;
1016}
1017
1018
1019
1020} // drain::
1021
1022#endif /* DRAIN_XML */
1023
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 from, char to, std::ostream &ostr)
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:195
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:156
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:91
virtual bool isSingular() const
Tell if this element should always have an explicit closing tag even when empty, like <STYLE></STYLE>
Definition XML.cpp:150
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:609
void setId()
Makes ID a visible attribute.
Definition XML.h:251
void setStyle(const std::string &key, const std::initializer_list< V > &l)
Set style of an element.
Definition XML.h:471
const std::string & getId() const
Returns ID of this element. Hopefully a unique ID...
Definition XML.h:270
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:387
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:725
static const std::map< char, std::string > & getAttributeConversionMap()
Characters that must be avoided in XML attribute values: ".
Definition XML.cpp:106
static const std::map< char, std::string > & getCTextConversionMap()
Characters to avoid in XML free text: <, >, {, },.
Definition XML.cpp:118
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:578
void setId(const TT &...args)
Concatenates arguments to an id.
Definition XML.h:264
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:762
static std::ostream & toStream(std::ostream &ostr, const TR &tree, const std::string &defaultTag="ELEM", int indent=0)
Definition XML.h:829
static T & xmlAddChild(T &tree, const std::string &key)
Definition XML.h:775
static N & xmlAssignNode(N &dst, const N &src)
Assign tree node (data) to another.
Definition XML.h:654
static TX & xmlAssign(TX &dst, const typename TX::xml_node_t &src)
Copy node data to tree.
Definition XML.h:632
void reset()
Keep the element type but clear style, class and string data.
Definition XML.cpp:143
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:487
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:161
static T & xmlAssign(T &tree, std::initializer_list< std::pair< const char *, const Variable > > l)
Tree.
Definition XML.h:693
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:282
void setAttribute(const std::string &key, const V &value)
"Final" implementation.
Definition XML.h:394
static T & xmlAssign(T &tree, const V &arg)
Assign property to a XML tree node.
Definition XML.h:681
bool hasClass(const V &cls) const
Definition XML.h:527
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:257
Definition DataSelector.cpp:1277
A container for a static dictionary of enumeration values.
Definition EnumFlags.h:69
Definition Type.h:542