1
2
3
4
5
6 #include <sngxml/dom/Element.hpp>
7 #include <soulng/util/Unicode.hpp>
8
9 namespace sngxml { namespace dom {
10
11 using namespace soulng::unicode;
12
13 std::u32string AttrValueEscape(const std::u32string& attributeValue, char32_t delimiter)
14 {
15 std::u32string result;
16 for (char32_t c : attributeValue)
17 {
18 switch (c)
19 {
20 case '<': result.append(U"<"); break;
21 case '&': result.append(U"&"); break;
22 case '"': if (delimiter == '"') result.append(U"""); else result.append(1, '"'); break;
23 case '\'': if (delimiter == '\'') result.append(U"'"); else result.append(1, '\''); break;
24 default: result.append(1, c); break;
25 }
26 }
27 return result;
28 }
29
30 std::u32string MakeXmlAttrValue(const std::u32string& attributeValue)
31 {
32 std::u32string result;
33 if (attributeValue.find('"') == std::u32string::npos)
34 {
35 result.append(1, '"');
36 result.append(AttrValueEscape(attributeValue, '"'));
37 result.append(1, '"');
38 }
39 else if (attributeValue.find('\'') == std::u32string::npos)
40 {
41 result.append(1, '\'');
42 result.append(AttrValueEscape(attributeValue, '\''));
43 result.append(1, '\'');
44 }
45 else
46 {
47 result.append(1, '"');
48 result.append(AttrValueEscape(attributeValue, '"'));
49 result.append(1, '"');
50 }
51 return result;
52 }
53
54 Attr::Attr() : Node(NodeType::attributeNode, U""), value(U"")
55 {
56 }
57
58 Attr::Attr(const std::u32string& name_, const std::u32string& value_) : Node(NodeType::attributeNode, name_), value(value_)
59 {
60 }
61
62 std::std::unique_ptr<Node>Attr::CloneNode(booldeep)
63 {
64 return std::unique_ptr<Node>(new Attr(Name(), value));
65 }
66
67 void Attr::Write(CodeFormatter& formatter)
68 {
69 formatter.Write(" " + ToUtf8(Name()) + "=");
70 formatter.Write(ToUtf8(MakeXmlAttrValue(value)));
71 }
72
73 Element::Element(const std::u32string& name_) : ParentNode(NodeType::elementNode, name_)
74 {
75 }
76
77 Element::Element(const std::u32string& name_, std::std::map<std::u32string, std::std::unique_ptr<Attr>>&&attributeMap_):ParentNode(NodeType::elementNode, name_), attributeMap(std::move(attributeMap_))
78 {
79 }
80
81 std::std::unique_ptr<Node>Element::CloneNode(booldeep)
82 {
83 std::unique_ptr<Node> clone(new Element(Name()));
84 ParentNode* cloneAsParent = static_cast<ParentNode*>(clone.get());
85 std::map<std::u32string, std::std::unique_ptr<Attr>>clonedAttributeMap;
86 for (const auto& p : attributeMap)
87 {
88 std::unique_ptr<Node> clonedAttrNode = p.second->CloneNode(false);
89 clonedAttrNode->InternalSetParent(cloneAsParent);
90 clonedAttributeMap[p.first] = std::unique_ptr<Attr>(static_cast<Attr*>(clonedAttrNode.release()));
91 }
92 Element* cloneAsElement = static_cast<Element*>(clone.get());
93 cloneAsElement->attributeMap = std::move(clonedAttributeMap);
94 if (deep)
95 {
96 CloneChildrenTo(cloneAsParent);
97 }
98 return clone;
99 }
100
101 bool Element::HasAttributes() const
102 {
103 return !attributeMap.empty();
104 }
105
106 void Element::Write(CodeFormatter& formatter)
107 {
108 if (HasChildNodes())
109 {
110 if (attributeMap.empty())
111 {
112 formatter.Write("<" + ToUtf8(Name()) + ">");
113 }
114 else
115 {
116 formatter.Write("<" + ToUtf8(Name()));
117 WriteAttributes(formatter);
118 formatter.Write(">");
119 }
120 bool prevPreserveSpace = formatter.PreserveSpace();
121 if (GetAttribute(U"xml:space") == U"preserve")
122 {
123 formatter.SetPreserveSpace(true);
124 }
125 bool preserveSpace = formatter.PreserveSpace() || !HasMultilineContent();
126 if (!preserveSpace)
127 {
128 formatter.WriteLine();
129 formatter.IncIndent();
130 }
131 ParentNode::Write(formatter);
132 if (!preserveSpace)
133 {
134 formatter.DecIndent();
135 formatter.WriteLine("</" + ToUtf8(Name()) + ">");
136 }
137 else if (prevPreserveSpace)
138 {
139 formatter.Write("</" + ToUtf8(Name()) + ">");
140 }
141 else
142 {
143 formatter.WriteLine("</" + ToUtf8(Name()) + ">");
144 }
145 formatter.SetPreserveSpace(prevPreserveSpace);
146 }
147 else
148 {
149 if (attributeMap.empty())
150 {
151 formatter.WriteLine("<" + ToUtf8(Name()) + "/>");
152 }
153 else
154 {
155 formatter.Write("<" + ToUtf8(Name()));
156 WriteAttributes(formatter);
157 formatter.WriteLine("/>");
158 }
159 }
160 }
161
162 void Element::WriteAttributes(CodeFormatter& formatter)
163 {
164 for (auto& p : attributeMap)
165 {
166 std::std::unique_ptr<Attr>&attr=p.second;
167 attr->Write(formatter);
168 }
169 }
170
171 bool Element::HasMultilineContent()
172 {
173 if (FirstChild() != LastChild()) return true;
174 Node* child = FirstChild();
175 if (child)
176 {
177 if (child->GetNodeType() == NodeType::elementNode || child->GetNodeType() == NodeType::documentNode) return true;
178 if (child->ValueContainsNewLine()) return true;
179 }
180 return false;
181 }
182
183 std::u32string Element::GetAttribute(const std::u32string& attrName) const
184 {
185 auto it = attributeMap.find(attrName);
186 if (it != attributeMap.cend())
187 {
188 return it->second->Value();
189 }
190 return std::u32string();
191 }
192
193 void Element::AddAttribute(std::std::unique_ptr<Attr>&&attr)
194 {
195 attributeMap[attr->Name()] = std::move(attr);
196 }
197
198 void Element::SetAttribute(const std::u32string& attrName, const std::u32string& attrValue)
199 {
200 attributeMap[attrName] = std::unique_ptr<Attr>(new Attr(attrName, attrValue));
201 }
202
203 void Element::RemoveAttribute(const std::u32string& attrName)
204 {
205 attributeMap.erase(attrName);
206 }
207
208 void Element::WalkAttribute(NodeOp& nodeOp)
209 {
210 for (const auto& p : attributeMap)
211 {
212 Attr* attr = p.second.get();
213 nodeOp.Apply(attr);
214 }
215 }
216
217 class ElementsByTagNameVisitor : public Visitor
218 {
219 public:
220 ElementsByTagNameVisitor(NodeList& elements_, const std::u32string& tagName_);
221 void BeginVisit(Element* elemnt) override;
222 private:
223 NodeList& elements;
224 std::u32string tagName;
225 };
226
227 ElementsByTagNameVisitor::ElementsByTagNameVisitor(NodeList& elements_, const std::u32string& tagName_) : elements(elements_), tagName(tagName_)
228 {
229 }
230
231 void ElementsByTagNameVisitor::BeginVisit(Element* element)
232 {
233 if (element->Name() == tagName)
234 {
235 elements.InternalAddNode(element);
236 }
237 }
238
239 NodeList Element::GetElementsByTagName(const std::u32string& tagName)
240 {
241 NodeList result;
242 ElementsByTagNameVisitor visitor(result, tagName);
243 Accept(visitor);
244 return result;
245 }
246
247 void Element::Accept(Visitor& visitor)
248 {
249 visitor.BeginVisit(this);
250 ParentNode::Accept(visitor);
251 visitor.EndVisit(this);
252 }
253
254 } }