Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / base / Xml.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/base/Xml.h
10  *
11 */
12 #ifndef ZYPP_BASE_XML_H
13 #define ZYPP_BASE_XML_H
14
15 #include <iosfwd>
16 #include <string>
17 #include <vector>
18 #include <list>
19 #include <set>
20 #include <map>
21
22 #include "zypp/base/Easy.h"
23 #include "zypp/base/String.h"
24 #include "zypp/parser/xml/XmlEscape.h"
25
26 ///////////////////////////////////////////////////////////////////
27 namespace zypp
28 {
29   ///////////////////////////////////////////////////////////////////
30   namespace xmlout
31   {
32     using xml::escape;
33     using xml::unescape;
34
35     /** \relates NodeAttr NODE ATTRIBUTE representation of types [asString] */
36     template <class Tp>
37     std::string asXmlNodeAttr( const Tp & val_r )
38     { return asString( val_r ); }
39
40     ///////////////////////////////////////////////////////////////////
41     /// \class NodeAttr
42     /// \brief (Key, Value) string pair of XML node attributes
43     struct NodeAttr : public std::pair<std::string,std::string>
44     {
45       typedef std::pair<std::string,std::string> Pair;
46
47       template <typename Tp>
48       NodeAttr( std::string key_r, const Tp & val_r )
49       : Pair( std::move(key_r), asXmlNodeAttr(val_r) )
50       {}
51
52       NodeAttr( std::string key_r, std::string val_r )
53       : Pair( std::move(key_r), std::move(val_r) )
54       {}
55     };
56     ///////////////////////////////////////////////////////////////////
57
58     ///////////////////////////////////////////////////////////////////
59     /// \class Node
60     /// \brief RAII writing a nodes start/end tag
61     /// \code
62     /// {
63     ///   Node node( std::cout, "node", { "attr", "val" } ); // <node attr="val">
64     ///   *node << "write nodes body...."
65     /// }                                                    // </node>
66     /// \endcode
67     /// \note If the \ref optionalContent flag is passed to the \c ctor, the start
68     /// node is kept open, until the first call to \ref operator*. The start node
69     /// is closed before returning the stream.
70     /// \code
71     /// {
72     ///   Node node( std::cout, "node", Node::optionalContent, { "attr", "val" } );
73     ///                                                      // <node attr="val"
74     /// }                                                    // />
75     /// {
76     ///   Node node( std::cout, "node", Node::optionalContent, { "attr", "val" } );
77     ///                                                      // <node attr="val"
78     ///   *node << "write nodes body...."                    // />write nodes body...
79     /// }                                                    // </node>
80     /// \endcode
81     ///
82     /// \note If the nodename is empty or starts with an \c !, a comment is written.
83     ///
84     struct Node
85     {
86       NON_COPYABLE_BUT_MOVE( Node );
87       typedef NodeAttr Attr;
88
89       struct OptionalContentType {};    ///< Ctor arg type
90       static constexpr OptionalContentType optionalContent = OptionalContentType();
91
92       /** Ctor taking nodename and attribute list */
93       Node( std::ostream & out_r, std::string name_r, const std::initializer_list<Attr> & attrs_r = {} )
94       : _out( out_r ), _name( std::move(name_r) ), _hasContent( true )
95       { printStart( attrs_r ); }
96
97       /** Convenience ctor for one attribute pair */
98       Node( std::ostream & out_r, std::string name_r, Attr attr_r )
99       : Node( out_r, std::move(name_r), { attr_r } )
100       {}
101
102       /** Optional content ctor taking nodename and attribute list */
103       Node( std::ostream & out_r, std::string name_r, OptionalContentType, const std::initializer_list<Attr> & attrs_r = {} )
104       : _out( out_r ), _name( std::move(name_r) ), _hasContent( false )
105       { printStart( attrs_r ); }
106
107       /** Optional content Convenience ctor for one attribute pair */
108       Node( std::ostream & out_r, std::string name_r, OptionalContentType, Attr attr_r )
109       : Node( out_r, std::move(name_r), optionalContent, { attr_r } )
110       {}
111
112       /** Dtor wrting end tag */
113       ~Node()
114       {
115         if ( isComment() )
116           _out << "-->";
117         else
118         {
119           if ( _hasContent )
120             _out << "</" << _name << ">";
121           else
122             _out << "/>";
123         }
124       }
125
126       /** Exception type thrown if attributes are added to a closed start node. */
127       struct HasContentException{};
128
129        /** Add additional attributes (requires OptionalContentType)
130         * \throw HasContentException If start node is already closed
131         */
132       Node & addAttr( const std::initializer_list<Attr> & attrs_r = {} )
133       {
134         if ( _hasContent )
135           throw HasContentException();
136         printAttr( attrs_r );
137         return *this;
138       }
139
140       /** \overload for one */
141       Node & addAttr( const Attr & attr_r )
142       { return addAttr( { attr_r } ); }
143
144
145       /** Return the output stream */
146       std::ostream & operator*()
147       {
148         if ( ! _hasContent )
149         {
150           _hasContent = true;
151           if ( isComment() )
152             _out << "|";
153           else
154             _out << ">";
155         }
156         return _out;
157       }
158
159     private:
160       void printStart( const std::initializer_list<Attr> & attrs_r )
161       {
162         if ( _name.empty() || _name[0] == '!' )
163         {
164           _out << "<!--" << _name;
165           _name.clear();        // a comment
166         }
167         else
168           _out << "<" << _name;
169
170         printAttr( attrs_r );
171
172         if ( !isComment() && _hasContent )
173           _out << ">";
174       }
175
176       void printAttr( const std::initializer_list<Attr> & attrs_r )
177       {
178         for ( const auto & pair : attrs_r )
179           _out << " " << pair.first << "=\"" << xml::escape( pair.second ) << "\"";
180       }
181
182       bool isComment() const
183       { return _name.empty();  }
184
185     private:
186       std::ostream & _out;
187       std::string _name;
188       bool _hasContent;
189     };
190     ///////////////////////////////////////////////////////////////////
191
192     /** \relates Node Write a leaf node without PCDATA
193      * \code
194      * <node attr="val"/>
195      * \endcode
196      */
197     inline std::ostream & node( std::ostream & out_r, const std::string & name_r, const std::initializer_list<Node::Attr> & attrs_r = {} )
198     {
199       Node( out_r, name_r, Node::optionalContent, attrs_r );
200       return out_r;
201     }
202     /** \overload for one attribute pair */
203     inline std::ostream & node( std::ostream & out_r, const std::string & name_r, Node::Attr attr_r )
204     { return node( out_r, name_r, { attr_r } ); }
205
206   } // namespace xmlout
207   ///////////////////////////////////////////////////////////////////
208
209   /// \name Default dumpAsXmlOn based on asString.
210   ///
211   //@{
212   template <class Tp>
213   inline std::ostream & dumpAsXmlOn( std::ostream & str, const Tp & obj, const std::string & name_r )
214   {
215     xmlout::Node guard( str, name_r, xmlout::Node::optionalContent );
216     const std::string & content( asString( obj ) );
217     if ( ! content.empty() ) *guard << content;
218     return str;
219   }
220   //@}
221   //
222 } // namespace zypp
223 ///////////////////////////////////////////////////////////////////
224 #endif // ZYPP_BASE_XML_H