aboutsummaryrefslogtreecommitdiff
path: root/include/cru/xml/XmlNode.h
blob: 737a2d8776c6bb9a0571ec60f77b7afb1cffa0e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#pragma once

#include "Base.h"

#include "cru/common/String.h"

#include <algorithm>
#include <optional>
#include <unordered_map>
#include <vector>

namespace cru::xml {
class XmlElementNode;
class XmlTextNode;

class CRU_XML_API XmlNode {
  friend XmlElementNode;

 public:
  enum class Type { Text, Element };

 protected:
  explicit XmlNode(Type type) : type_(type) {}

 public:
  CRU_DELETE_COPY(XmlNode)
  CRU_DELETE_MOVE(XmlNode)

  virtual ~XmlNode() = default;

  Type GetType() const { return type_; }
  XmlElementNode* GetParent() const { return parent_; }

  virtual XmlNode* Clone() const = 0;

  bool IsTextNode() const { return type_ == Type::Text; }
  bool IsElementNode() const { return type_ == Type::Element; }

  XmlElementNode* AsElement();
  XmlTextNode* AsText();
  const XmlElementNode* AsElement() const;
  const XmlTextNode* AsText() const;

 private:
  const Type type_;
  XmlElementNode* parent_ = nullptr;
};

class CRU_XML_API XmlTextNode : public XmlNode {
 public:
  XmlTextNode() : XmlNode(Type::Text) {}
  explicit XmlTextNode(String text)
      : XmlNode(Type::Text), text_(std::move(text)) {}

  CRU_DELETE_COPY(XmlTextNode)
  CRU_DELETE_MOVE(XmlTextNode)

  ~XmlTextNode() override = default;

 public:
  String GetText() const { return text_; }
  void SetText(String text) { text_ = std::move(text); }

  XmlNode* Clone() const override { return new XmlTextNode(text_); }

 private:
  String text_;
};

class CRU_XML_API XmlElementNode : public XmlNode {
 public:
  XmlElementNode() : XmlNode(Type::Element) {}
  explicit XmlElementNode(String tag,
                          std::unordered_map<String, String> attributes = {})
      : XmlNode(Type::Element),
        tag_(std::move(tag)),
        attributes_(std::move(attributes)) {}

  CRU_DELETE_COPY(XmlElementNode)
  CRU_DELETE_MOVE(XmlElementNode)

  ~XmlElementNode() override;

 public:
  String GetTag() const { return tag_; }
  void SetTag(String tag) { tag_ = std::move(tag); }
  const std::unordered_map<String, String>& GetAttributes() const {
    return attributes_;
  }
  void SetAttributes(std::unordered_map<String, String> attributes) {
    attributes_ = std::move(attributes);
  }
  const std::vector<XmlNode*> GetChildren() const { return children_; }

  Index GetChildCount() const { return children_.size(); }
  String GetAttribute(const String& key) const { return attributes_.at(key); }
  String GetAttributeCaseInsensitive(const String& key) const {
    return *GetOptionalAttributeCaseInsensitive(key);
  }
  std::optional<String> GetOptionalAttribute(const String& key) const {
    auto it = attributes_.find(key);
    if (it == attributes_.end()) {
      return std::nullopt;
    }

    return it->second;
  }
  std::optional<String> GetOptionalAttributeCaseInsensitive(
      const String& key) const {
    for (auto it = attributes_.begin(); it != attributes_.end(); ++it) {
      if (it->first.CaseInsensitiveCompare(key) == 0) {
        return it->second;
      }
    }

    return std::nullopt;
  }

  XmlNode* GetChildAt(Index index) const { return children_[index]; }

  void AddAttribute(String key, String value);
  void AddChild(XmlNode* child);

  Index GetChildElementCount() const;
  XmlElementNode* GetFirstChildElement() const;

  XmlNode* Clone() const override;

 private:
  String tag_;
  std::unordered_map<String, String> attributes_;
  std::vector<XmlNode*> children_;
};

}  // namespace cru::xml