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