1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  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& attributeValuechar32_t delimiter)
 14 {
 15     std::u32string result;
 16     for (char32_t c : attributeValue)
 17     {
 18         switch (c)
 19         {
 20         case '<': result.append(U"&lt;"); break;
 21         case '&': result.append(U"&amp;"); break;
 22         case '"': if (delimiter == '"') result.append(U"&quot;"); else result.append(1'"'); break;
 23         case '\'': if (delimiter == '\'') result.append(U"&apos;"); else result.append(1'\''); break;
 24         default: result.append(1c); 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::attributeNodeU"")value(U"")
 55 {
 56 }
 57 
 58 Attr::Attr(const std::u32string& name_const std::u32string& value_) : Node(NodeType::attributeNodename_)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::elementNodename_)
 74 {
 75 }
 76 
 77 Element::Element(const std::u32string& name_std::std::map<std::u32stringstd::std::unique_ptr<Attr>>&&attributeMap_):ParentNode(NodeType::elementNodename_)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::u32stringstd::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& attrNameconst std::u32string& attrValue)
199 {
200     attributeMap[attrName] = std::unique_ptr<Attr>(new Attr(attrNameattrValue));
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(resulttagName);
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 } } // namespace sngxml::dom