GammaLib  1.7.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GXmlElement.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GXmlElement.cpp - XML element node class implementation *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2018 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GXmlElement.cpp
23  * @brief XML element node class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include "GTools.hpp"
32 #include "GException.hpp"
33 #include "GFilename.hpp"
34 #include "GXmlDocument.hpp"
35 #include "GXmlElement.hpp"
36 
37 /* __ Method name definitions ____________________________________________ */
38 #define G_ATTRIBUTE "GXmlElement::attribute(int&)"
39 #define G_PARSE_START "GXmlElement::parse_start(std::string&)"
40 #define G_PARSE_STOP "GXmlElement::parse_stop(std::string&)"
41 #define G_PARSE_ATTRIBUTE "GXmlElement::parse_attribute(size_t*, "\
42  "std::string&)"
43 
44 /* __ Constants __________________________________________________________ */
45 const int g_indent = 2; //!< Indent for XML file writing
46 
47 /* __ Macros _____________________________________________________________ */
48 
49 /* __ Coding definitions _________________________________________________ */
50 
51 /* __ Debug definitions __________________________________________________ */
52 
53 
54 /*==========================================================================
55  = =
56  = Constructors/destructors =
57  = =
58  ==========================================================================*/
59 
60 /***********************************************************************//**
61  * @brief Void constructor
62  ***************************************************************************/
64 {
65  // Initialise members
66  init_members();
67 
68  // Return
69  return;
70 }
71 
72 
73 /***********************************************************************//**
74  * @brief Copy constructor
75  *
76  * @param[in] node XML element.
77  ***************************************************************************/
79 {
80  // Initialise members
81  init_members();
82 
83  // Copy members
84  copy_members(node);
85 
86  // Return
87  return;
88 }
89 
90 
91 /***********************************************************************//**
92  * @brief Segment constructor
93  *
94  * @param[in] segment XML segment.
95  *
96  * Constructs a XML element from a text @p segment. The text segment is
97  * parsed and the element name and attributes are extracted using the
98  * parse_start() method.
99  ***************************************************************************/
100 GXmlElement::GXmlElement(const std::string& segment) : GXmlNode()
101 {
102  // Initialise members
103  init_members();
104 
105  // Parse start element
106  parse_start(segment);
107 
108  // Return
109  return;
110 }
111 
112 
113 /***********************************************************************//**
114  * @brief Destructor
115  ***************************************************************************/
117 {
118  // Free members
119  free_members();
120 
121  // Return
122  return;
123 }
124 
125 
126 /*==========================================================================
127  = =
128  = Operators =
129  = =
130  ==========================================================================*/
131 
132 /***********************************************************************//**
133  * @brief Assignment operator
134  *
135  * @param[in] node XML element.
136  * @return XML element.
137  ***************************************************************************/
139 {
140  // Execute only if object is not identical
141  if (this != &node) {
142 
143  // Copy base class members
144  this->GXmlNode::operator=(node);
145 
146  // Free members
147  free_members();
148 
149  // Initialise members
150  init_members();
151 
152  // Copy members
153  copy_members(node);
154 
155  } // endif: object was not identical
156 
157  // Return
158  return *this;
159 }
160 
161 
162 /*==========================================================================
163  = =
164  = Public methods =
165  = =
166  ==========================================================================*/
167 
168  /***********************************************************************//**
169  * @brief Clear XML element
170  *
171  * Resets the XML element to a clean initial state.
172  ***************************************************************************/
174 {
175  // Free class members (base and derived classes, derived class first)
176  free_members();
177  this->GXmlNode::free_members();
178 
179  // Initialise members
180  this->GXmlNode::init_members();
181  init_members();
182 
183  // Return
184  return;
185 }
186 
187 
188 /***********************************************************************//**
189  * @brief Clone XML element
190  *
191  * @return Pointer to deep copy of XML element.
192  ***************************************************************************/
194 {
195  // Clone element
196  return new GXmlElement(*this);
197 }
198 
199 
200 /***********************************************************************//**
201  * @brief Return attribute
202  *
203  * @param[in] index Attribute index.
204  * @return Pointer to attribute.
205  ***************************************************************************/
206 const GXmlAttribute* GXmlElement::attribute(const int& index) const
207 {
208  // Compile option: raise exception if index is out of range
209  #if defined(G_RANGE_CHECK)
210  if (index < 0 || index >= attributes()) {
212  }
213  #endif
214 
215  // Return pointer to attribute
216  return (m_attr[index]);
217 }
218 
219 
220 /***********************************************************************//**
221  * @brief Return attribute value
222  *
223  * @param[in] name Attribute name.
224  * @return String containing attribute value.
225  *
226  * Returns the value of the attribute @p name. If the requested attribute was
227  * not found an empty string is returned.
228  ***************************************************************************/
229 std::string GXmlElement::attribute(const std::string& name) const
230 {
231  // Initialise empty value (i.e. attribute not found)
232  std::string value = "";
233 
234  // Search attribute value in list of attributes
235  for (int i = 0; i < m_attr.size(); ++i) {
236  if (m_attr[i]->name() == name) {
237  value = m_attr[i]->value();
238  break;
239  }
240  }
241 
242  // Return value
243  return value;
244 }
245 
246 
247 /***********************************************************************//**
248  * @brief Set attribute value
249  *
250  * @param[in] name Attribute name.
251  * @param[in] value Attribute value.
252  *
253  * Sets an attribute of the element. If the attribute name exists the value
254  * is modified. If the attribute does not yet exist it is created and
255  * added to the list of attributes.
256  *
257  * Note that this logical assures that only one attribute with a given name
258  * will exist in the element.
259  ***************************************************************************/
260 void GXmlElement::attribute(const std::string& name, const std::string& value)
261 {
262  // Initialise attribute NULL pointer
263  GXmlAttribute* attr = NULL;
264 
265  // Search attribute name in list of attributes
266  for (int i = 0; i < m_attr.size(); ++i) {
267  if (m_attr[i]->name() == name) {
268  attr = m_attr[i];
269  break;
270  }
271  }
272 
273  // If no attribute with specified name has been found then add a new
274  // attribute to the list of attributes
275  if (attr == NULL) {
276  attr = new GXmlAttribute;
277  attr->name(name);
278  m_attr.push_back(attr);
279  }
280 
281  // Set or update value of attribute
282  attr->value(value);
283 
284  // Return
285  return;
286 }
287 
288 
289 /***********************************************************************//**
290  * @brief Check if element has a given attribute
291  *
292  * @param[in] name Attribute name.
293  * @return True if attribute exists, false otherwise.
294  *
295  * Checks whether the element contains an attribute with @p name. If the
296  * attribute was found true is returned, false otherwise.
297  ***************************************************************************/
298 bool GXmlElement::has_attribute(const std::string& name) const
299 {
300  // Initialise found flag
301  bool found = false;
302 
303  // Search attribute value in list of attributes
304  for (int i = 0; i < m_attr.size(); ++i) {
305  if (m_attr[i]->name() == name) {
306  found = true;
307  break;
308  }
309  }
310 
311  // Return found flag
312  return found;
313 }
314 
315 
316 /***********************************************************************//**
317  * @brief Remove attribute from element
318  *
319  * @param[in] name Attribute name.
320  *
321  * Remove the attribute with @p name from the XML element. If the requested
322  * attribute was not found the method does nothing.
323  ***************************************************************************/
324 void GXmlElement::remove_attribute(const std::string& name)
325 {
326  // Do nothing if there are no attributes
327  if (!m_attr.empty()) {
328 
329  // Store number of attributes.
330  int num = m_attr.size();
331 
332  // Search attribute name in list of attributes and erase attribute
333  // when it has been found. Note that if several attributes with the
334  // same name exist (which should never be the case!), only the
335  // first attribute is removed
336  for (int i = 0; i < num; ++i) {
337  if (m_attr[i]->name() == name) {
338  m_attr.erase(m_attr.begin() + i);
339  break;
340  }
341  }
342 
343  } // endif: there were attributes
344 
345  // Return
346  return;
347 }
348 
349 
350 /***********************************************************************//**
351  * @brief Write element into URL
352  *
353  * @param[in] url Unified Resource Locator.
354  * @param[in] indent Text indentation.
355  *
356  * Writes the element into a Unified Resource Locator.
357  ***************************************************************************/
358 void GXmlElement::write(GUrl& url, const int& indent) const
359 {
360  // Prepend indentation
361  for (int k = 0; k < indent; ++k) {
362  url.printf(" ");
363  }
364 
365  // Write element name into URL
366  url.printf("<%s", m_name.c_str());
367 
368  // Write attributes into URL
369  for (int k = 0; k < m_attr.size(); ++k) {
370  m_attr[k]->write(url);
371  }
372 
373  // If there are no children then write an empty tag
374  if (is_empty()) {
375  url.printf(" />\n");
376  }
377 
378  // ... otherwise finish start tag, write children and write end tag
379  else {
380 
381  // Case A: The element contains a single text leaf
382  if ((m_nodes.size() == 1) && (m_nodes[0]->type() == NT_TEXT)) {
383 
384  // Finish start tag
385  url.printf(">");
386 
387  // Write text leaf
388  m_nodes[0]->write(url, 0);
389 
390  // Write end tag
391  url.printf("</%s>\n", m_name.c_str());
392  }
393 
394  // Case B: ... otherwise it contains markup
395  else {
396 
397  // Finish start tag
398  url.printf(">\n");
399 
400  // Write children in file
401  for (int i = 0; i < m_nodes.size(); ++i) {
402  m_nodes[i]->write(url, indent+g_indent);
403  if (m_nodes[i]->type() == NT_TEXT) {
404  url.printf("\n");
405  }
406  }
407 
408  // Write end tag
409  for (int k = 0; k < indent; ++k) {
410  url.printf(" ");
411  }
412  url.printf("</%s>\n", m_name.c_str());
413 
414  } // endelse: element contained markup
415 
416  } // endelse: finished start tag
417 
418  // Return
419  return;
420 }
421 
422 
423 /***********************************************************************//**
424  * @brief Print XML element
425  *
426  * @param[in] chatter Chattiness.
427  * @param[in] indent Text indentation.
428  * @return String containing XML element
429  ***************************************************************************/
430 std::string GXmlElement::print(const GChatter& chatter,
431  const int& indent) const
432 {
433  // Allocate result string
434  std::string result;
435 
436  // Continue only if chatter is not silent
437  if (chatter != SILENT) {
438 
439  // Initialise result string
440  result = gammalib::fill(" ", indent);
441 
442  // Append element to string
443  result.append("GXmlElement::"+m_name);
444  for (int k = 0; k < m_attr.size(); ++k) {
445  result.append(m_attr[k]->print(chatter));
446  }
447 
448  // Append children
449  for (int i = 0; i < m_nodes.size(); ++i) {
450  result.append("\n" + m_nodes[i]->print(chatter, indent+g_indent));
451  }
452 
453  } // endif: chatter was not silent
454 
455  // Return
456  return result;
457 }
458 
459 
460 /*==========================================================================
461  = =
462  = Private methods =
463  = =
464  ==========================================================================*/
465 
466 /***********************************************************************//**
467  * @brief Initialise class members
468  ***************************************************************************/
470 {
471  // Initialise members
472  m_name.clear();
473  m_attr.clear();
474 
475  // Return
476  return;
477 }
478 
479 
480 /***********************************************************************//**
481  * @brief Copy class members
482  *
483  * @param[in] node XML element.
484  *
485  * This method copies all class members. XML attributes are cloned.
486  ***************************************************************************/
488 {
489  // Copy members
490  m_name = node.m_name;
491 
492  // Copy attribute container
493  m_attr.clear();
494  for (int i = 0; i < node.m_attr.size(); ++i) {
495  m_attr.push_back((node.m_attr[i]->clone()));
496  }
497 
498  // Return
499  return;
500 }
501 
502 
503 /***********************************************************************//**
504  * @brief Delete class members
505  *
506  * As container classes that hold pointers need to handle themselves the
507  * proper deallocation of memory, we loop here over all pointers and make
508  * sure that we deallocate the associated nodes.
509  ***************************************************************************/
511 {
512  // Free attributes
513  for (int i = 0; i < m_attr.size(); ++i) {
514  delete m_attr[i];
515  m_attr[i] = NULL;
516  }
517 
518  // Return
519  return;
520 }
521 
522 
523 /***********************************************************************//**
524  * @brief Parse element start segment string
525  *
526  * @param[in] segment Segment string.
527  *
528  * @exception GException::xml_syntax_error
529  * XML syntax error.
530  *
531  * Parse the segment string and set class members based on the information
532  * that is found. The method also performs syntax checking. It does not
533  * require brackets to be set.
534  ***************************************************************************/
535 void GXmlElement::parse_start(const std::string& segment)
536 {
537  // Initialize position check
538  std::size_t pos_start = 0;
539 
540  // Get length of segment
541  int n = segment.length();
542 
543  // Throw an error is segment is empty
544  if (n < 1) {
546  "no element name specified");
547  }
548 
549  // If string starts with brackets then check that the brackets are
550  // valid comment brackets
551  if (segment[0] == '<') {
552  if (n < 2 || (segment.compare(0,1,"<") != 0) ||
553  (segment.compare(n-1,1,">") != 0)) {
555  "invalid tag brackets");
556  }
557  pos_start = 1;
558  } // endif: there were brackets
559 
560  // Extract element name
561  std::size_t pos = segment.find_first_of("\x20\x09\x0d\x0a>", 1);
562  if (pos == pos_start) {
564  "no whitespace allowed before element name");
565  }
566  if (pos == std::string::npos) {
567  if (pos_start == 1) {
569  "element name not found");
570  }
571  }
572  m_name = segment.substr(pos_start, pos-pos_start);
573 
574  // Extract attributes
575  while (pos != std::string::npos) {
576  parse_attribute(&pos, segment);
577  }
578 
579  // Return
580  return;
581 }
582 
583 
584 /***********************************************************************//**
585  * @brief Parse element stop segment string
586  *
587  * @param[in] segment Segment string.
588  *
589  * @exception GException::xml_syntax_error
590  * XML syntax error.
591  *
592  * Parse the stop segment string and verify the syntax.
593  ***************************************************************************/
594 void GXmlElement::parse_stop(const std::string& segment)
595 {
596  // Get length of segment
597  int n = segment.length();
598 
599  // Check on existence of brackets
600  if (n < 3 || (segment.compare(0,2,"</") != 0) ||
601  (segment.compare(n-1,1,">") != 0)) {
603  "incorrect or missing tag brackets");
604  }
605 
606  // Extract and verify element name
607  size_t pos = segment.find_first_of("\x20\x09\x0d\x0a>", 2);
608  if (pos == 2) {
610  "no whitespace allowed after \"</\"");
611  }
612  if (pos == std::string::npos) {
614  "element name not found");
615  }
616  std::string name = segment.substr(2, pos-2);
617  if (name != m_name) {
619  "invalid name in element stop tag"
620  " (found \""+name+"\", expected \""+m_name+"\"");
621  }
622 
623  // Verify that no further characters exist in element stop tag
624  size_t pos2 = segment.find_first_of("\x20\x09\x0d\x0a>", pos);
625  if (pos2 != n-1) {
627  "invalid characters found after element name");
628  }
629 
630  // Return
631  return;
632 }
633 
634 
635 /***********************************************************************//**
636  * @brief Parse element attribute
637  *
638  * @param[in] pos Start position in string.
639  * @param[in] segment Segment string.
640  *
641  * @exception GException::xml_syntax_error
642  * XML syntax error.
643  *
644  * Parse the segment string for one attribute, and if attribute was found,
645  * attach it to element.
646  *
647  * @todo Verify XML validity of attribute name and value
648  ***************************************************************************/
649 void GXmlElement::parse_attribute(size_t* pos, const std::string& segment)
650 {
651  // Main loop
652  do {
653  // Get substring for error message
654  std::string error = segment.substr(*pos, segment.length()-*pos);
655 
656  // Find first character of name substring
657  std::size_t pos_name_start = segment.find_first_not_of("\x20\x09\x0d\x0a/>?", *pos);
658  if (pos_name_start == std::string::npos) {
659  *pos = std::string::npos;
660  continue;
661  }
662 
663  // Find end of name substring
664  std::size_t pos_name_end = segment.find_first_of("\x20\x09\x0d\x0a=", pos_name_start);
665  if (pos_name_end == std::string::npos) {
667  "invalid or missing attribute name");
668  }
669 
670  // Find '=' character
671  std::size_t pos_equal = segment.find_first_of("=", pos_name_end);
672  if (pos_equal == std::string::npos) {
674  "\"=\" sign not found for attribute");
675  }
676 
677  // Find start of value substring
678  std::size_t pos_value_start = segment.find_first_of("\x22\x27", pos_equal);
679  if (pos_value_start == std::string::npos) {
681  "invalid or missing attribute value start hyphen");
682  }
683 
684  // Save hyphen character and step forward one character
685  std::string hyphen = segment.substr(pos_value_start, 1);
686  pos_value_start++;
687  if (pos_value_start >= segment.length()) {
689  "invalid or missing attribute value");
690  }
691 
692  // Find end of value substring
693  std::size_t pos_value_end = segment.find_first_of(hyphen, pos_value_start);
694  if (pos_value_end == std::string::npos) {
696  "invalid or missing attribute value end hyphen");
697  }
698 
699  // Get name substring
700  std::size_t n_name = pos_name_end - pos_name_start;
701  if (n_name < 1) {
703  "invalid or missing attribute name");
704  }
705  std::string name = segment.substr(pos_name_start, n_name);
706 
707  //@todo Check XML validity of attribute name
708 
709  // Get value substring length
710  std::size_t n_value = pos_value_end - pos_value_start;
711  //if (n_value < 0) {
712  // throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
713  // "invalid or missing attribute value");
714  //}
715  std::string value = segment.substr(pos_value_start-1, n_value+2);
716 
717  //@todo Check XML validity of attribute value
718 
719  // Allocate, set and append new attribute to element
720  GXmlAttribute* attr = new GXmlAttribute(name, value);
721  m_attr.push_back(attr);
722 
723  // Update segment pointer
724  pos_value_end++;
725  if (pos_value_end >= segment.length()) {
726  pos_value_end = std::string::npos;
727  }
728  *pos = pos_value_end;
729 
730  } while (0);
731 
732  // Return
733  return;
734 }
Abstract XML node base class.
Definition: GXmlNode.hpp:57
GXmlNode & operator=(const GXmlNode &node)
Assignment operator.
Definition: GXmlNode.cpp:118
virtual std::string print(const GChatter &chatter=NORMAL, const int &indent=0) const
Print XML element.
std::vector< GXmlNode * > m_nodes
Pointer to child nodes.
Definition: GXmlNode.hpp:123
XML element node class interface definition.
std::vector< GXmlAttribute * > m_attr
Attributes.
Definition: GXmlElement.hpp:91
void init_members(void)
Initialise class members.
Definition: GXmlNode.cpp:948
XML element node class.
Definition: GXmlElement.hpp:47
XML attribute class.
Gammalib tools definition.
void free_members(void)
Delete class members.
Definition: GXmlNode.cpp:989
const std::string & name(void) const
Return XML element name.
virtual void clear(void)
Clear XML element.
#define G_PARSE_START
Definition: GXmlElement.cpp:39
virtual ~GXmlElement(void)
Destructor.
Abstract URL base class.
Definition: GUrl.hpp:44
void parse_attribute(size_t *pos, const std::string &segment)
Parse element attribute.
const GXmlAttribute * attribute(const int &index) const
Return attribute.
void remove_attribute(const std::string &name)
Remove attribute from element.
bool has_attribute(const std::string &name) const
Check if element has a given attribute.
void init_members(void)
Initialise class members.
const std::string & name(void) const
Return attribute name.
const int g_indent
Indent for XML file writing.
Definition: GXmlElement.cpp:45
GChatter
Definition: GTypemaps.hpp:33
XML document node class interface definition.
void copy_members(const GXmlElement &node)
Copy class members.
virtual void printf(const char *format,...)=0
GXmlElement(void)
Void constructor.
Definition: GXmlElement.cpp:63
virtual NodeType type(void) const
Return XML node type.
const std::string & value(void) const
Return attribute value.
virtual bool is_empty(void) const
Signals if node has no child nodes.
Definition: GXmlNode.hpp:145
void free_members(void)
Delete class members.
GXmlElement & operator=(const GXmlElement &node)
Assignment operator.
std::string m_name
Element name.
Definition: GXmlElement.hpp:90
#define G_PARSE_ATTRIBUTE
Definition: GXmlElement.cpp:41
Exception handler interface definition.
virtual void write(GUrl &url, const int &indent=0) const
Write element into URL.
void parse_start(const std::string &segment)
Parse element start segment string.
#define G_PARSE_STOP
Definition: GXmlElement.cpp:40
void parse_stop(const std::string &segment)
Parse element stop segment string.
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition: GTools.cpp:921
#define G_ATTRIBUTE
Definition: GXmlElement.cpp:38
int attributes(void) const
Return number of attributes.
Filename class interface definition.
virtual GXmlElement * clone(void) const
Clone XML element.