1
2
3
4
5
6 #include <sngxml/dom/Document.hpp>
7 #include <sngxml/dom/Element.hpp>
8 #include <sngxml/dom/Exception.hpp>
9 #include <soulng/util/Unicode.hpp>
10 #include <soulng/util/Error.hpp>
11
12 namespace sngxml { namespace dom {
13
14 using namespace soulng::unicode;
15
16 Document::Document() : ParentNode(NodeType::documentNode, U"document"), documentElement(nullptr), indexValid(false), xmlStandalone(false)
17 {
18 }
19
20 void Document::Write(CodeFormatter& formatter)
21 {
22 if (!xmlVersion.empty() && !xmlEncoding.empty())
23 {
24 formatter.WriteLine("<?xml version=\"" + ToUtf8(xmlVersion) + "\" encoding=\"" + ToUtf8(xmlEncoding) + "\"?>");
25 }
26 ParentNode::Write(formatter);
27 }
28
29 std::std::unique_ptr<Node>Document::CloneNode(booldeep)
30 {
31 std::unique_ptr<Node> clonedDocument = std::unique_ptr<Node>(new Document());
32 if (deep)
33 {
34 ParentNode* parentNode = static_cast<ParentNode*>(clonedDocument.get());
35 CloneChildrenTo(parentNode);
36 }
37 return clonedDocument;
38 }
39
40 Node* Document::InsertBefore(std::std::unique_ptr<Node>&&newChild, Node*refChild)
41 {
42 CheckValidInsert(newChild.get(), refChild);
43 if (newChild->GetNodeType() == NodeType::elementNode)
44 {
45 Assert(documentElement == nullptr, "document element is not null");
46 documentElement = static_cast<Element*>(newChild.get());
47 }
48 return ParentNode::InsertBefore(std::move(newChild), refChild);
49 }
50
51 std::std::unique_ptr<Node>Document::ReplaceChild(std::std::unique_ptr<Node>&&newChild, Node*oldChild)
52 {
53 if (!oldChild)
54 {
55 throw DomException("could not replace node: given old child is null");
56 }
57 if (oldChild->Parent() != this)
58 {
59 throw DomException("could not replace node: given old child is not child of this node");
60 }
61 CheckValidInsert(newChild.get(), nullptr);
62 if (newChild->GetNodeType() == NodeType::elementNode)
63 {
64 std::unique_ptr<Node> removed = RemoveChild(oldChild);
65 AppendChild(std::move(newChild));
66 return removed;
67 }
68 else
69 {
70 return ParentNode::ReplaceChild(std::move(newChild), oldChild);
71 }
72 }
73
74 std::std::unique_ptr<Node>Document::RemoveChild(Node*oldChild)
75 {
76 if (!oldChild)
77 {
78 throw DomException("could not remove node: given old child is null");
79 }
80 if (oldChild->Parent() != this)
81 {
82 throw DomException("could not remove node: given old child is not child of this node");
83 }
84 if (oldChild->GetNodeType() == NodeType::elementNode)
85 {
86 documentElement = nullptr;
87 }
88 return ParentNode::RemoveChild(oldChild);
89 }
90
91 Node* Document::AppendChild(std::std::unique_ptr<Node>&&newChild)
92 {
93 CheckValidInsert(newChild.get(), nullptr);
94 if (newChild->GetNodeType() == NodeType::elementNode)
95 {
96 Assert(documentElement == nullptr, "document element is not null");
97 documentElement = static_cast<Element*>(newChild.get());
98 }
99 return ParentNode::AppendChild(std::move(newChild));
100 }
101
102 void Document::InternalInvalidateIndex()
103 {
104 indexValid = false;
105 }
106
107 void Document::Accept(Visitor& visitor)
108 {
109 visitor.BeginVisit(this);
110 ParentNode::Accept(visitor);
111 visitor.EndVisit(this);
112 }
113
114 class BuildIndexVisitor : public Visitor
115 {
116 public:
117 BuildIndexVisitor(std::std::unordered_map<std::u32string, Element*>&elementsByIdMap_);
118 void BeginVisit(Element* element) override;
119 private:
120 std::std::unordered_map<std::u32string, Element*>&elementsByIdMap;
121 };
122
123 BuildIndexVisitor::BuildIndexVisitor(std::std::unordered_map<std::u32string, Element*>&elementsByIdMap_):elementsByIdMap(elementsByIdMap_)
124 {
125 }
126
127 void BuildIndexVisitor::BeginVisit(Element* element)
128 {
129 const std::u32string& id = element->GetAttribute(U"id");
130 if (!id.empty())
131 {
132 elementsByIdMap[id] = element;
133 }
134 }
135
136 Element* Document::GetElementById(const std::u32string& elementId)
137 {
138 if (!indexValid)
139 {
140 elementsByIdMap.clear();
141 BuildIndexVisitor visitor(elementsByIdMap);
142 Accept(visitor);
143 indexValid = true;
144 }
145 std::unordered_map<std::u32string, Element*>::const_iterator it = elementsByIdMap.find(elementId);
146 if (it != elementsByIdMap.cend())
147 {
148 Element* element = it->second;
149 return element;
150 }
151 return nullptr;
152 }
153
154 void Document::CheckValidInsert(Node* node, Node* refNode)
155 {
156 if (node->GetNodeType() == NodeType::elementNode)
157 {
158 if (refNode != nullptr || documentElement != nullptr)
159 {
160 throw DomException("attempt to insert a second element to a document");
161 }
162 }
163 }
164
165 } }