1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.Xml
10 {
11 public class DocumentHandler : XmlContentHandler
12 {
13 public DocumentHandler() : textFileIndex(-1)
14 {
15 }
16 public Document* GetDocument()
17 {
18 return document.Release();
19 }
20 [nodiscard]
21 public override Result<bool> StartDocument(const System.Lex.Span& span, int fileIndex)
22 {
23 document.Reset(new Document(span, fileIndex));
24 currentParentNode = document.Get();
25 return Result<bool>(true);
26 }
27 [nodiscard]
28 public override Result<bool> EndDocument()
29 {
30 return Result<bool>(true);
31 }
32 [nodiscard]
33 public override Result<bool> Version(const ustring& xmlVersion)
34 {
35 auto result = ToUtf8(xmlVersion);
36 if (result.Error())
37 {
38 return Result<bool>(ErrorId(result.GetErrorId()));
39 }
40 document->SetXmlVersion(result.Value());
41 return Result<bool>(true);
42 }
43 [nodiscard]
44 public override Result<bool> Standalone(bool standalone)
45 {
46 document->SetXmlStandalone(standalone);
47 return Result<bool>(true);
48 }
49 [nodiscard]
50 public override Result<bool> Encoding(const ustring& encoding)
51 {
52 auto result = ToUtf8(encoding);
53 if (result.Error())
54 {
55 return Result<bool>(ErrorId(result.GetErrorId()));
56 }
57 document->SetXmlEncoding(result.Value());
58 return Result<bool>(true);
59 }
60 [nodiscard]
61 public override Result<bool> HandleText(const System.Lex.Span& span, int fileIndex, const ustring& text)
62 {
63 if (!textSpan.IsValid())
64 {
65 textSpan = span;
66 }
67 else
68 {
69 textSpan.Union(span);
70 }
71 textFileIndex = fileIndex;
72 auto result = ToUtf8(text);
73 if (result.Error())
74 {
75 return Result<bool>(ErrorId(result.GetErrorId()));
76 }
77 textContent.Append(result.Value());
78 return Result<bool>(true);
79 }
80 [nodiscard]
81 public override Result<bool> HandleComment(const System.Lex.Span& span, int fileIndex, const ustring& comment)
82 {
83 auto result = AddTextContent();
84 if (result.Error())
85 {
86 return Result<bool>(ErrorId(result.GetErrorId()));
87 }
88 auto commentResult = ToUtf8(comment);
89 if (commentResult.Error())
90 {
91 return Result<bool>(ErrorId(commentResult.GetErrorId()));
92 }
93 currentParentNode->AppendChild(new Comment(span, fileIndex, commentResult.Value()));
94 return Result<bool>(true);
95 }
96 [nodiscard]
97 public override Result<bool> HandlePI(const System.Lex.Span& span, int fileIndex, const ustring& target, const ustring& data)
98 {
99 auto result = AddTextContent();
100 if (result.Error())
101 {
102 return Result<bool>(ErrorId(result.GetErrorId()));
103 }
104 auto targetResult = ToUtf8(target);
105 if (targetResult.Error())
106 {
107 return Result<bool>(ErrorId(targetResult.GetErrorId()));
108 }
109 auto dataResult = ToUtf8(data);
110 if (dataResult.Error())
111 {
112 return Result<bool>(ErrorId(dataResult.GetErrorId()));
113 }
114 currentParentNode->AppendChild(new ProcessingInstruction(span, fileIndex, targetResult.Value(), dataResult.Value()));
115 return Result<bool>(true);
116 }
117 [nodiscard]
118 public override Result<bool> HandleCDataSection(const System.Lex.Span& span, int fileIndex, const ustring& cdata)
119 {
120 auto result = AddTextContent();
121 if (result.Error())
122 {
123 return Result<bool>(ErrorId(result.GetErrorId()));
124 }
125 auto cdataResult = ToUtf8(cdata);
126 if (cdataResult.Error())
127 {
128 return Result<bool>(ErrorId(cdataResult.GetErrorId()));
129 }
130 currentParentNode->AppendChild(new CDataSection(span, fileIndex, cdataResult.Value()));
131 return Result<bool>(true);
132 }
133 [nodiscard]
134 public override Result<bool> StartElement(const System.Lex.Span& span, int fileIndex,
135 const ustring& namespaceUri, const ustring& localName, const ustring& qualifiedName, const Attributes& attributes)
136 {
137 auto result = AddTextContent(true);
138 if (result.Error())
139 {
140 return Result<bool>(ErrorId(result.GetErrorId()));
141 }
142 parentNodeStack.Push(currentParentNode);
143 elementStack.Push(Rvalue(currentElement));
144 auto qualifiedNameResult = ToUtf8(qualifiedName);
145 if (qualifiedNameResult.Error())
146 {
147 return Result<bool>(ErrorId(qualifiedNameResult.GetErrorId()));
148 }
149 currentElement.Reset(new Element(span, fileIndex, qualifiedNameResult.Value()));
150 currentParentNode = currentElement.Get();
151 for (const auto& attribute : attributes)
152 {
153 auto qualifiedNameResult = ToUtf8(attribute.QualifiedName());
154 if (qualifiedNameResult.Error())
155 {
156 return Result<bool>(ErrorId(qualifiedNameResult.GetErrorId()));
157 }
158 auto valueResult = ToUtf8(attribute.Value());
159 if (valueResult.Error())
160 {
161 return Result<bool>(ErrorId(valueResult.GetErrorId()));
162 }
163 currentElement->SetAttribute(attribute.Span(), fileIndex, qualifiedNameResult.Value(), valueResult.Value());
164 }
165 currentElement->SetOwnerDocument(document.Get());
166 if (!namespaceUri.IsEmpty())
167 {
168 auto namespaceUriResult = ToUtf8(namespaceUri);
169 if (namespaceUriResult.Error())
170 {
171 return Result<bool>(ErrorId(namespaceUriResult.GetErrorId()));
172 }
173 currentElement->SetNamespaceUri(namespaceUriResult.Value());
174 }
175 return Result<bool>(true);
176 }
177 [nodiscard]
178 public override Result<bool> EndElement(const ustring& namespaceUri, const ustring& localName, const ustring& qualifiedName)
179 {
180 auto result = AddTextContent();
181 if (result.Error())
182 {
183 return Result<bool>(ErrorId(result.GetErrorId()));
184 }
185 if (parentNodeStack.IsEmpty())
186 {
187 string errorMessage = "parent node stack is empty";
188 int errorId = AllocateError(errorMessage);
189 return Result<bool>(ErrorId(errorId));
190 }
191 currentParentNode = parentNodeStack.Pop();
192 currentParentNode->AppendChild(currentElement.Release());
193 if (elementStack.IsEmpty())
194 {
195 string errorMessage = "element stack is empty";
196 int errorId = AllocateError(errorMessage);
197 return Result<bool>(ErrorId(errorId));
198 }
199 currentElement = elementStack.Pop();
200 return Result<bool>(true);
201 }
202 [nodiscard]
203 public override Result<bool> SkippedEntity(const ustring& entityName)
204 {
205 auto result = AddTextContent();
206 if (result.Error())
207 {
208 return Result<bool>(ErrorId(result.GetErrorId()));
209 }
210 auto entityNameResult = ToUtf8(entityName);
211 if (entityNameResult.Error())
212 {
213 return Result<bool>(ErrorId(entityNameResult.GetErrorId()));
214 }
215 currentParentNode->AppendChild(MakeEntityReference(entityNameResult.Value()));
216 return Result<bool>(true);
217 }
218 [nodiscard]
219 private Result<bool> AddTextContent()
220 {
221 return AddTextContent(false);
222 }
223 [nodiscard]
224 private Result<bool> AddTextContent(bool addSpace)
225 {
226 if (!currentElement.IsNull())
227 {
228 auto utf32Result = ToUtf32(textContent);
229 if (utf32Result.Error())
230 {
231 return Result<bool>(ErrorId(utf32Result.GetErrorId()));
232 }
233 auto trimResult = TrimAll(utf32Result.Value());
234 if (trimResult.Error())
235 {
236 return Result<bool>(ErrorId(trimResult.GetErrorId()));
237 }
238 auto utf8Result = ToUtf8(trimResult.Value());
239 if (utf8Result.Error())
240 {
241 return Result<bool>(ErrorId(utf8Result.GetErrorId()));
242 }
243 textContent = utf8Result.Value();
244 if (!textContent.IsEmpty())
245 {
246 if (addSpace)
247 {
248 textContent.Append(' ');
249 }
250 currentElement->AppendChild(new Text(textSpan, textFileIndex, textContent));
251 textSpan = System.Lex.Span();
252 }
253 }
254 textContent.Clear();
255 return Result<bool>(true);
256 }
257 private UniquePtr<Document> document;
258 private ParentNode* currentParentNode;
259 private Stack<ParentNode*> parentNodeStack;
260 private UniquePtr<Element> currentElement;
261 private Stack<UniquePtr<Element>> elementStack;
262 private System.Lex.Span textSpan;
263 private int textFileIndex;
264 private string textContent;
265 }
266 }