basic-xml 1.0.1
Very basic loader/writer of XML tags with attributes and content.
node.cpp
Go to the documentation of this file.
1// Copyright (c) 2019-2024 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/basic-xml
4// contact@m2osw.com
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19
30// self
31//
32#include "basic-xml/node.h"
33
34#include "basic-xml/exception.h"
35#include "basic-xml/type.h"
36
37
38// snapdev
39//
40#include <snapdev/not_reached.h>
41#include <snapdev/trim_string.h>
42#include <snapdev/string_replace_many.h>
43
44
45// C++
46//
47#include <iomanip>
48
49
50// last include
51//
52#include <snapdev/poison.h>
53
54
55
56namespace basic_xml
57{
58
59
60
61node::node(std::string const & name)
62 : f_name(name)
63{
64 if(!is_token(name))
65 {
66 throw invalid_token(
67 "\""
68 + snapdev::string_replace_many(name, {{std::string("\0", 1), "\\0"}})
69 + "\" is not a valid token for a tag name.");
70 }
71}
72
73
74std::string const & node::tag_name() const
75{
76 return f_name;
77}
78
79
80std::string node::text(bool trim) const
81{
82 if(trim)
83 {
84 return snapdev::trim_string(f_text);
85 }
86 return f_text;
87}
88
89
90void node::set_text(std::string const & text)
91{
92 f_text = text;
93}
94
95
96void node::append_text(std::string const & text)
97{
98 f_text += text;
99}
100
101
102node::attribute_map_t node::all_attributes() const
103{
104 return f_attributes;
105}
106
107
108std::string node::attribute(std::string const & name) const
109{
110 auto const it(f_attributes.find(name));
111 if(it == f_attributes.end())
112 {
113 return std::string();
114 }
115 return it->second;
116}
117
118
119void node::set_attribute(std::string const & name, std::string const & value)
120{
121 if(!is_token(name))
122 {
123 throw invalid_token("\"" + name + "\" is not a valid token for an attribute name.");
124 }
125 f_attributes[name] = value;
126}
127
128
129void node::append_child(pointer_t n)
130{
131 if(n->f_next != nullptr
132 || n->f_previous.lock() != nullptr
133 || n->f_parent.lock() != nullptr)
134 {
135 throw node_already_in_tree("Somehow you are trying to add a child node of a node that was already added to a tree of nodes.");
136 }
137 if(n == root())
138 {
139 throw node_is_root("Trying to append the root node within the sub-tree.");
140 }
141
142 auto l(last_child());
143 if(l == nullptr)
144 {
145 f_child = n;
146 }
147 else
148 {
149 l->f_next = n;
150 n->f_previous = l;
151 }
152
153 n->f_parent = shared_from_this();
154}
155
156
157node::pointer_t node::root() const
158{
159 node::pointer_t result(const_cast<node *>(this)->shared_from_this());
160 for(;;)
161 {
162 node::pointer_t p(result->parent());
163 if(p == nullptr)
164 {
165 return result;
166 }
167 result = p;
168 }
169 snapdev::NOT_REACHED();
170}
171
172
173node::pointer_t node::parent() const
174{
175 auto result(f_parent.lock());
176 return result;
177}
178
179
180node::pointer_t node::first_child() const
181{
182 return f_child;
183}
184
185
186node::pointer_t node::last_child() const
187{
188 if(f_child == nullptr)
189 {
190 return node::pointer_t();
191 }
192
193 pointer_t l(f_child);
194 while(l->f_next != nullptr)
195 {
196 l = l->f_next;
197 }
198
199 return l;
200}
201
202
203node::pointer_t node::next() const
204{
205 return f_next;
206}
207
208
209node::pointer_t node::previous() const
210{
211 return f_previous.lock();
212}
213
214
215std::string convert_to_entity(std::string const & raw, std::string const & which)
216{
217 std::string result;
218 result.reserve(raw.length() + 10);
219 for(auto const & c : raw)
220 {
221 if(which.find(c) == std::string::npos)
222 {
223 result += c;
224 }
225 else
226 {
227 switch(c)
228 {
229 case '&':
230 result += "&amp;";
231 break;
232
233 case '<':
234 result += "&lt;";
235 break;
236
237 case '>':
238 result += "&gt;";
239 break;
240
241 case '"':
242 result += "&quot;";
243 break;
244
245 case '\'':
246 result += "&apos;";
247 break;
248
249 }
250 }
251 }
252 return result;
253}
254
255std::ostream & operator << (std::ostream & out, node const & n)
256{
257 out << '<';
258 out << n.tag_name();
259 for(auto const & a : n.all_attributes())
260 {
261 // use attr='...' if the string includes one or more `"` and no
262 // apostrophe otherwise use attr="..." and convert any `"` with &quot;
263 // that way we never need &apos;
264 //
265 char const quote(a.second.find('"') != std::string::npos
266 && a.second.find('\'') == std::string::npos ? '\'' : '"');
267 out << ' '
268 << a.first
269 << '='
270 << quote
271 << (quote == '"'
272 ? convert_to_entity(a.second, "&<>\"")
273 : convert_to_entity(a.second, "&<>"))
274 << quote;
275 }
276 auto child(n.first_child());
277 bool empty(child == nullptr && n.text().empty() && n.parent() != nullptr);
278 if(empty)
279 {
280 out << '/';
281 }
282 out << '>';
283 if(child != nullptr)
284 {
285 while(child != nullptr)
286 {
287 out << *child; // recursive call
288 child = child->next();
289 }
290 }
291 if(!n.text().empty())
292 {
293 // in this case we can safely keep the " as is instead of &quot;
294 //
295 out << convert_to_entity(n.text(), "&<>");
296 }
297 if(!empty)
298 {
299 out << "</"
300 << n.tag_name()
301 << '>';
302 }
303
304 return out;
305}
306
307
308
309} // namespace prinbee
310// vim: ts=4 sw=4 et
Snap! Database exceptions.
Node for the basic XML tree.
bool is_token(std::string const &token)
Verify that token is a valid string.
Definition type.cpp:204
Database file implementation.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.