Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / XmlWellFormedWriterHelpers.cs
blob144d806e5a81402161a33e95cae95c5e0e540a4f
2 //------------------------------------------------------------------------------
3 // <copyright file="XmlWellFormedWriterHelpers.cs" company="Microsoft">
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // </copyright>
6 // <owner current="true" primary="true">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 using System;
10 using System.Text;
11 using System.Diagnostics;
12 using System.Collections.Generic;
14 namespace System.Xml {
16 internal partial class XmlWellFormedWriter : XmlWriter {
19 // Private types
21 class NamespaceResolverProxy : IXmlNamespaceResolver {
22 XmlWellFormedWriter wfWriter;
24 internal NamespaceResolverProxy(XmlWellFormedWriter wfWriter) {
25 this.wfWriter = wfWriter;
28 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope) {
29 throw new NotImplementedException();
31 string IXmlNamespaceResolver.LookupNamespace(string prefix) {
32 return wfWriter.LookupNamespace(prefix);
35 string IXmlNamespaceResolver.LookupPrefix(string namespaceName) {
36 return wfWriter.LookupPrefix(namespaceName);
40 partial struct ElementScope {
42 internal int prevNSTop;
43 internal string prefix;
44 internal string localName;
45 internal string namespaceUri;
46 internal XmlSpace xmlSpace;
47 internal string xmlLang;
49 internal void Set(string prefix, string localName, string namespaceUri, int prevNSTop) {
50 this.prevNSTop = prevNSTop;
51 this.prefix = prefix;
52 this.namespaceUri = namespaceUri;
53 this.localName = localName;
54 this.xmlSpace = (System.Xml.XmlSpace)(int)-1;
55 this.xmlLang = null;
58 internal void WriteEndElement(XmlRawWriter rawWriter) {
59 rawWriter.WriteEndElement(prefix, localName, namespaceUri);
62 internal void WriteFullEndElement(XmlRawWriter rawWriter) {
63 rawWriter.WriteFullEndElement(prefix, localName, namespaceUri);
67 enum NamespaceKind {
68 Written,
69 NeedToWrite,
70 Implied,
71 Special,
74 partial struct Namespace {
76 internal string prefix;
77 internal string namespaceUri;
78 internal NamespaceKind kind;
79 internal int prevNsIndex;
81 internal void Set(string prefix, string namespaceUri, NamespaceKind kind) {
82 this.prefix = prefix;
83 this.namespaceUri = namespaceUri;
84 this.kind = kind;
85 this.prevNsIndex = -1;
88 internal void WriteDecl(XmlWriter writer, XmlRawWriter rawWriter) {
89 Debug.Assert(kind == NamespaceKind.NeedToWrite);
90 if (null != rawWriter) {
91 rawWriter.WriteNamespaceDeclaration(prefix, namespaceUri);
93 else {
94 if (prefix.Length == 0) {
95 writer.WriteStartAttribute(string.Empty, "xmlns", XmlReservedNs.NsXmlNs);
97 else {
98 writer.WriteStartAttribute("xmlns", prefix, XmlReservedNs.NsXmlNs);
100 writer.WriteString(namespaceUri);
101 writer.WriteEndAttribute();
106 struct AttrName {
107 internal string prefix;
108 internal string namespaceUri;
109 internal string localName;
110 internal int prev;
112 internal void Set(string prefix, string localName, string namespaceUri) {
113 this.prefix = prefix;
114 this.namespaceUri = namespaceUri;
115 this.localName = localName;
116 this.prev = 0;
119 internal bool IsDuplicate(string prefix, string localName, string namespaceUri) {
120 return ((this.localName == localName)
121 && ((this.prefix == prefix) || (this.namespaceUri == namespaceUri)));
125 enum SpecialAttribute {
126 No = 0,
127 DefaultXmlns,
128 PrefixedXmlns,
129 XmlSpace,
130 XmlLang
133 partial class AttributeValueCache {
135 enum ItemType {
136 EntityRef,
137 CharEntity,
138 SurrogateCharEntity,
139 Whitespace,
140 String,
141 StringChars,
142 Raw,
143 RawChars,
144 ValueString,
147 class Item {
148 internal ItemType type;
149 internal object data;
151 internal Item() { }
153 internal void Set(ItemType type, object data) {
154 this.type = type;
155 this.data = data;
159 class BufferChunk {
160 internal char[] buffer;
161 internal int index;
162 internal int count;
164 internal BufferChunk(char[] buffer, int index, int count) {
165 this.buffer = buffer;
166 this.index = index;
167 this.count = count;
171 StringBuilder stringValue = new StringBuilder();
172 string singleStringValue; // special-case for a single WriteString call
173 Item[] items;
174 int firstItem;
175 int lastItem = -1;
177 internal string StringValue {
178 get {
179 if (singleStringValue != null) {
180 return singleStringValue;
182 else {
183 return stringValue.ToString();
188 internal void WriteEntityRef(string name) {
189 if (singleStringValue != null) {
190 StartComplexValue();
193 switch (name) {
194 case "lt":
195 stringValue.Append('<');
196 break;
197 case "gt":
198 stringValue.Append('>');
199 break;
200 case "quot":
201 stringValue.Append('"');
202 break;
203 case "apos":
204 stringValue.Append('\'');
205 break;
206 case "amp":
207 stringValue.Append('&');
208 break;
209 default:
210 stringValue.Append('&');
211 stringValue.Append(name);
212 stringValue.Append(';');
213 break;
216 AddItem(ItemType.EntityRef, name);
219 internal void WriteCharEntity(char ch) {
220 if (singleStringValue != null) {
221 StartComplexValue();
223 stringValue.Append(ch);
224 AddItem(ItemType.CharEntity, ch);
227 internal void WriteSurrogateCharEntity(char lowChar, char highChar) {
228 if (singleStringValue != null) {
229 StartComplexValue();
231 stringValue.Append(highChar);
232 stringValue.Append(lowChar);
233 AddItem(ItemType.SurrogateCharEntity, new char[] { lowChar, highChar });
236 internal void WriteWhitespace(string ws) {
237 if (singleStringValue != null) {
238 StartComplexValue();
240 stringValue.Append(ws);
241 AddItem(ItemType.Whitespace, ws);
244 internal void WriteString(string text) {
245 if (singleStringValue != null) {
246 StartComplexValue();
248 else {
249 // special-case for a single WriteString
250 if (lastItem == -1) {
251 singleStringValue = text;
252 return;
256 stringValue.Append(text);
257 AddItem(ItemType.String, text);
260 internal void WriteChars(char[] buffer, int index, int count) {
261 if (singleStringValue != null) {
262 StartComplexValue();
264 stringValue.Append(buffer, index, count);
265 AddItem(ItemType.StringChars, new BufferChunk(buffer, index, count));
268 internal void WriteRaw(char[] buffer, int index, int count) {
269 if (singleStringValue != null) {
270 StartComplexValue();
272 stringValue.Append(buffer, index, count);
273 AddItem(ItemType.RawChars, new BufferChunk(buffer, index, count));
276 internal void WriteRaw(string data) {
277 if (singleStringValue != null) {
278 StartComplexValue();
280 stringValue.Append(data);
281 AddItem(ItemType.Raw, data);
284 internal void WriteValue(string value) {
285 if (singleStringValue != null) {
286 StartComplexValue();
288 stringValue.Append(value);
289 AddItem(ItemType.ValueString, value);
292 internal void Replay(XmlWriter writer) {
293 if (singleStringValue != null) {
294 writer.WriteString(singleStringValue);
295 return;
298 BufferChunk bufChunk;
299 for (int i = firstItem; i <= lastItem; i++) {
300 Item item = items[i];
301 switch (item.type) {
302 case ItemType.EntityRef:
303 writer.WriteEntityRef((string)item.data);
304 break;
305 case ItemType.CharEntity:
306 writer.WriteCharEntity((char)item.data);
307 break;
308 case ItemType.SurrogateCharEntity:
309 char[] chars = (char[])item.data;
310 writer.WriteSurrogateCharEntity(chars[0], chars[1]);
311 break;
312 case ItemType.Whitespace:
313 writer.WriteWhitespace((string)item.data);
314 break;
315 case ItemType.String:
316 writer.WriteString((string)item.data);
317 break;
318 case ItemType.StringChars:
319 bufChunk = (BufferChunk)item.data;
320 writer.WriteChars(bufChunk.buffer, bufChunk.index, bufChunk.count);
321 break;
322 case ItemType.Raw:
323 writer.WriteRaw((string)item.data);
324 break;
325 case ItemType.RawChars:
326 bufChunk = (BufferChunk)item.data;
327 writer.WriteChars(bufChunk.buffer, bufChunk.index, bufChunk.count);
328 break;
329 case ItemType.ValueString:
330 writer.WriteValue((string)item.data);
331 break;
332 default:
333 Debug.Assert(false, "Unexpected ItemType value.");
334 break;
339 // This method trims whitespaces from the beginnig and the end of the string and cached writer events
340 internal void Trim() {
341 // if only one string value -> trim the write spaces directly
342 if (singleStringValue != null) {
343 singleStringValue = XmlConvert.TrimString(singleStringValue);
344 return;
347 // trim the string in StringBuilder
348 string valBefore = stringValue.ToString();
349 string valAfter = XmlConvert.TrimString(valBefore);
350 if (valBefore != valAfter) {
351 stringValue = new StringBuilder(valAfter);
354 // trim the beginning of the recorded writer events
355 XmlCharType xmlCharType = XmlCharType.Instance;
357 int i = firstItem;
358 while (i == firstItem && i <= lastItem) {
359 Item item = items[i];
360 switch (item.type) {
361 case ItemType.Whitespace:
362 firstItem++;
363 break;
364 case ItemType.String:
365 case ItemType.Raw:
366 case ItemType.ValueString:
367 item.data = XmlConvert.TrimStringStart((string)item.data);
368 if (((string)item.data).Length == 0) {
369 // no characters left -> move the firstItem index to exclude it from the Replay
370 firstItem++;
372 break;
373 case ItemType.StringChars:
374 case ItemType.RawChars:
375 BufferChunk bufChunk = (BufferChunk)item.data;
376 int endIndex = bufChunk.index + bufChunk.count;
377 while (bufChunk.index < endIndex && xmlCharType.IsWhiteSpace(bufChunk.buffer[bufChunk.index])) {
378 bufChunk.index++;
379 bufChunk.count--;
381 if (bufChunk.index == endIndex) {
382 // no characters left -> move the firstItem index to exclude it from the Replay
383 firstItem++;
385 break;
387 i++;
390 // trim the end of the recorded writer events
391 i = lastItem;
392 while (i == lastItem && i >= firstItem) {
393 Item item = items[i];
394 switch (item.type) {
395 case ItemType.Whitespace:
396 lastItem--;
397 break;
398 case ItemType.String:
399 case ItemType.Raw:
400 case ItemType.ValueString:
401 item.data = XmlConvert.TrimStringEnd((string)item.data);
402 if (((string)item.data).Length == 0) {
403 // no characters left -> move the lastItem index to exclude it from the Replay
404 lastItem--;
406 break;
407 case ItemType.StringChars:
408 case ItemType.RawChars:
409 BufferChunk bufChunk = (BufferChunk)item.data;
410 while (bufChunk.count > 0 && xmlCharType.IsWhiteSpace(bufChunk.buffer[bufChunk.index + bufChunk.count - 1])) {
411 bufChunk.count--;
413 if (bufChunk.count == 0) {
414 // no characters left -> move the lastItem index to exclude it from the Replay
415 lastItem--;
417 break;
419 i--;
423 internal void Clear() {
424 singleStringValue = null;
425 lastItem = -1;
426 firstItem = 0;
427 stringValue.Length = 0;
430 private void StartComplexValue() {
431 Debug.Assert(singleStringValue != null);
432 Debug.Assert(lastItem == -1);
434 stringValue.Append( singleStringValue );
435 AddItem(ItemType.String, singleStringValue);
437 singleStringValue = null;
440 void AddItem(ItemType type, object data) {
441 int newItemIndex = lastItem + 1;
442 if (items == null) {
443 items = new Item[4];
445 else if (items.Length == newItemIndex) {
446 Item[] newItems = new Item[newItemIndex * 2];
447 Array.Copy(items, newItems, newItemIndex);
448 items = newItems;
450 if (items[newItemIndex] == null) {
451 items[newItemIndex] = new Item();
453 items[newItemIndex].Set(type, data);
454 lastItem = newItemIndex;