node-attr.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #pragma once
  2. #include "xml-document-decl.h"
  3. #include "libxml-guards.h"
  4. #include <util/stream/str.h>
  5. #include <util/string/cast.h>
  6. namespace NXml {
  7. #define THROW(x, y) ythrow yexception() << #x << ": " << y
  8. // libxml defines unsigned char -> xmlChar,
  9. // and all functions use xmlChar.
  10. inline static const char* CAST2CHAR(const xmlChar* x) {
  11. return reinterpret_cast<const char*>(x);
  12. }
  13. inline static const xmlChar* XMLCHAR(const char* x) {
  14. return reinterpret_cast<const xmlChar*>(x);
  15. }
  16. template <class T>
  17. void TNode::AttrInternal(TCharPtr& value, T& res, TStringBuf errContext) const {
  18. try {
  19. res = FromString<T>(CAST2CHAR(value.Get()));
  20. } catch (TFromStringException&) {
  21. THROW(XmlException, "Failed to convert string " << TString{TStringBuf(CAST2CHAR(value.Get())).substr(0, 50)}.Quote() << " from '" << errContext << "' to requested type");
  22. }
  23. }
  24. template <>
  25. inline void TNode::AttrInternal(TCharPtr& value, TString& res, TStringBuf /*errContext*/) const {
  26. TString tmp(CAST2CHAR(value.Get()));
  27. res.swap(tmp);
  28. }
  29. template <class T>
  30. T TNode::Attr(TZtStringBuf name) const {
  31. TCharPtr value(xmlGetProp(NodePointer, XMLCHAR(name.c_str())));
  32. if (!value) {
  33. THROW(AttributeNotFound, Path() << "@" << name);
  34. }
  35. T t;
  36. AttrInternal(value, t, name);
  37. return t;
  38. }
  39. template <class T>
  40. TMaybe<T> TNode::TryAttr(TZtStringBuf name) const {
  41. TCharPtr value(xmlGetProp(NodePointer, XMLCHAR(name.c_str())));
  42. if (!value) {
  43. return Nothing();
  44. }
  45. T t;
  46. AttrInternal(value, t, name);
  47. return t;
  48. }
  49. template <class T>
  50. T TNode::Attr(TZtStringBuf name, const T& defvalue) const {
  51. TCharPtr attr(xmlGetProp(NodePointer, XMLCHAR(name.c_str())));
  52. if (!attr) {
  53. return defvalue;
  54. }
  55. T t;
  56. AttrInternal(attr, t, name);
  57. return t;
  58. }
  59. template <class T>
  60. void TNode::Attr(TZtStringBuf name, T& value) const {
  61. TCharPtr attr(xmlGetProp(NodePointer, XMLCHAR(name.c_str())));
  62. if (!attr) {
  63. THROW(AttributeNotFound, Path() << name);
  64. }
  65. AttrInternal(attr, value, name);
  66. }
  67. template <class T>
  68. void TNode::Attr(TZtStringBuf name, T& value, const T& defvalue) const {
  69. TCharPtr attr(xmlGetProp(NodePointer, XMLCHAR(name.c_str())));
  70. if (!attr) {
  71. value = defvalue;
  72. } else {
  73. AttrInternal(attr, value, name);
  74. }
  75. }
  76. template <class T>
  77. T TNode::Value() const {
  78. if (!NodePointer || xmlIsBlankNode(NodePointer)) {
  79. THROW(NodeIsBlank, Path());
  80. }
  81. TCharPtr val(xmlNodeGetContent(NodePointer));
  82. T t;
  83. AttrInternal(val, t, this->Name());
  84. return t;
  85. }
  86. template <class T>
  87. T TNode::Value(const T& defvalue) const {
  88. if (!NodePointer || xmlIsBlankNode(NodePointer)) {
  89. return defvalue;
  90. }
  91. TCharPtr val(xmlNodeGetContent(NodePointer));
  92. T t;
  93. AttrInternal(val, t, this->Name());
  94. return t;
  95. }
  96. template <class T>
  97. typename std::enable_if<!std::is_convertible_v<T, TStringBuf>, void>::type
  98. TNode::SetValue(const T& value) {
  99. TStringStream ss;
  100. ss << value;
  101. SetValue(ss.Str());
  102. }
  103. inline void TNode::SetValue(TStringBuf value) {
  104. xmlNodeSetContent(NodePointer, XMLCHAR(""));
  105. xmlNodeAddContentLen(NodePointer, XMLCHAR(value.data()), value.size());
  106. }
  107. inline void TNode::SetAttr(TZtStringBuf name, TZtStringBuf value) {
  108. xmlAttr* attr = xmlSetProp(NodePointer, XMLCHAR(name.c_str()), XMLCHAR(value.c_str()));
  109. if (!attr) {
  110. THROW(XmlException, "Can't set node attribute <"
  111. << name
  112. << "> to <"
  113. << value
  114. << ">");
  115. }
  116. }
  117. template <class T>
  118. typename std::enable_if<!std::is_convertible_v<T, TZtStringBuf>, void>::type
  119. TNode::SetAttr(TZtStringBuf name, const T& value) {
  120. TStringStream ss;
  121. ss << value;
  122. SetAttr(name, TZtStringBuf(ss.Str()));
  123. }
  124. inline void TNode::SetAttr(TZtStringBuf name) {
  125. xmlAttr* attr = xmlSetProp(NodePointer, XMLCHAR(name.c_str()), nullptr);
  126. if (!attr) {
  127. THROW(XmlException, "Can't set node empty attribute <"
  128. << name
  129. << ">");
  130. }
  131. }
  132. inline void TNode::DelAttr(TZtStringBuf name) {
  133. if (xmlUnsetProp(NodePointer, XMLCHAR(name.c_str())) < 0)
  134. THROW(XmlException, "Can't delete node attribute <"
  135. << name
  136. << ">");
  137. }
  138. template <class T>
  139. typename std::enable_if<!std::is_convertible_v<T, TZtStringBuf>, TNode>::type
  140. TNode::AddChild(TZtStringBuf name, const T& value) {
  141. TStringStream ss;
  142. ss << value;
  143. return AddChild(name, TZtStringBuf(ss.Str()));
  144. }
  145. inline TNode TNode::AddChild(TZtStringBuf name, TZtStringBuf value) {
  146. if (IsNull()) {
  147. THROW(XmlException, "addChild [name=" << name << ", value=" << value
  148. << "]: can't add child to null node");
  149. }
  150. xmlNode* child = nullptr;
  151. if (value.empty()) {
  152. child = xmlNewTextChild(NodePointer, nullptr, XMLCHAR(name.c_str()), nullptr);
  153. } else {
  154. child = xmlNewTextChild(
  155. NodePointer, nullptr, XMLCHAR(name.c_str()), XMLCHAR(value.c_str()));
  156. }
  157. if (!child) {
  158. THROW(XmlException, "addChild [name=" << name << ", value=" << value
  159. << "]: xmlNewTextChild returned NULL");
  160. }
  161. return TNode(DocPointer, child);
  162. }
  163. template <class T>
  164. typename std::enable_if<!std::is_convertible_v<T, TStringBuf>, TNode>::type
  165. TNode::AddText(const T& value) {
  166. TStringStream ss;
  167. ss << value;
  168. return AddText(ss.Str());
  169. }
  170. inline TNode TNode::AddText(TStringBuf value) {
  171. if (IsNull()) {
  172. THROW(XmlException, "addChild [value=" << value
  173. << "]: can't add child to null node");
  174. }
  175. xmlNode* child = xmlNewTextLen((xmlChar*)value.data(), value.size());
  176. child = xmlAddChild(NodePointer, child);
  177. if (!child) {
  178. THROW(XmlException, "addChild [value=" << value
  179. << "]: xmlNewTextChild returned NULL");
  180. }
  181. return TNode(DocPointer, child);
  182. }
  183. }