1 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 //------------------------------------------------------------------------------ 8 9 using System; 10 using System.Text; 11 using System.Diagnostics; 12 using System.Collections.Generic; 13 14 namespace System.Xml { 15 16 internal partial class XmlWellFormedWriter : XmlWriter { 17 18 // 19 // Private types 20 // 21 class NamespaceResolverProxy : IXmlNamespaceResolver { 22 XmlWellFormedWriter wfWriter; 23 NamespaceResolverProxy(XmlWellFormedWriter wfWriter)24 internal NamespaceResolverProxy(XmlWellFormedWriter wfWriter) { 25 this.wfWriter = wfWriter; 26 } 27 IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope)28 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope) { 29 throw new NotImplementedException(); 30 } IXmlNamespaceResolver.LookupNamespace(string prefix)31 string IXmlNamespaceResolver.LookupNamespace(string prefix) { 32 return wfWriter.LookupNamespace(prefix); 33 } 34 IXmlNamespaceResolver.LookupPrefix(string namespaceName)35 string IXmlNamespaceResolver.LookupPrefix(string namespaceName) { 36 return wfWriter.LookupPrefix(namespaceName); 37 } 38 } 39 40 partial struct ElementScope { 41 42 internal int prevNSTop; 43 internal string prefix; 44 internal string localName; 45 internal string namespaceUri; 46 internal XmlSpace xmlSpace; 47 internal string xmlLang; 48 SetSystem.Xml.XmlWellFormedWriter.ElementScope49 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; 56 } 57 WriteEndElementSystem.Xml.XmlWellFormedWriter.ElementScope58 internal void WriteEndElement(XmlRawWriter rawWriter) { 59 rawWriter.WriteEndElement(prefix, localName, namespaceUri); 60 } 61 WriteFullEndElementSystem.Xml.XmlWellFormedWriter.ElementScope62 internal void WriteFullEndElement(XmlRawWriter rawWriter) { 63 rawWriter.WriteFullEndElement(prefix, localName, namespaceUri); 64 } 65 } 66 67 enum NamespaceKind { 68 Written, 69 NeedToWrite, 70 Implied, 71 Special, 72 } 73 74 partial struct Namespace { 75 76 internal string prefix; 77 internal string namespaceUri; 78 internal NamespaceKind kind; 79 internal int prevNsIndex; 80 SetSystem.Xml.XmlWellFormedWriter.Namespace81 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; 86 } 87 WriteDeclSystem.Xml.XmlWellFormedWriter.Namespace88 internal void WriteDecl(XmlWriter writer, XmlRawWriter rawWriter) { 89 Debug.Assert(kind == NamespaceKind.NeedToWrite); 90 if (null != rawWriter) { 91 rawWriter.WriteNamespaceDeclaration(prefix, namespaceUri); 92 } 93 else { 94 if (prefix.Length == 0) { 95 writer.WriteStartAttribute(string.Empty, "xmlns", XmlReservedNs.NsXmlNs); 96 } 97 else { 98 writer.WriteStartAttribute("xmlns", prefix, XmlReservedNs.NsXmlNs); 99 } 100 writer.WriteString(namespaceUri); 101 writer.WriteEndAttribute(); 102 } 103 } 104 } 105 106 struct AttrName { 107 internal string prefix; 108 internal string namespaceUri; 109 internal string localName; 110 internal int prev; 111 SetSystem.Xml.XmlWellFormedWriter.AttrName112 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; 117 } 118 IsDuplicateSystem.Xml.XmlWellFormedWriter.AttrName119 internal bool IsDuplicate(string prefix, string localName, string namespaceUri) { 120 return ((this.localName == localName) 121 && ((this.prefix == prefix) || (this.namespaceUri == namespaceUri))); 122 } 123 } 124 125 enum SpecialAttribute { 126 No = 0, 127 DefaultXmlns, 128 PrefixedXmlns, 129 XmlSpace, 130 XmlLang 131 } 132 133 partial class AttributeValueCache { 134 135 enum ItemType { 136 EntityRef, 137 CharEntity, 138 SurrogateCharEntity, 139 Whitespace, 140 String, 141 StringChars, 142 Raw, 143 RawChars, 144 ValueString, 145 } 146 147 class Item { 148 internal ItemType type; 149 internal object data; 150 Item()151 internal Item() { } 152 Set(ItemType type, object data)153 internal void Set(ItemType type, object data) { 154 this.type = type; 155 this.data = data; 156 } 157 } 158 159 class BufferChunk { 160 internal char[] buffer; 161 internal int index; 162 internal int count; 163 BufferChunk(char[] buffer, int index, int count)164 internal BufferChunk(char[] buffer, int index, int count) { 165 this.buffer = buffer; 166 this.index = index; 167 this.count = count; 168 } 169 } 170 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; 176 177 internal string StringValue { 178 get { 179 if (singleStringValue != null) { 180 return singleStringValue; 181 } 182 else { 183 return stringValue.ToString(); 184 } 185 } 186 } 187 WriteEntityRef(string name)188 internal void WriteEntityRef(string name) { 189 if (singleStringValue != null) { 190 StartComplexValue(); 191 } 192 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; 214 } 215 216 AddItem(ItemType.EntityRef, name); 217 } 218 WriteCharEntity(char ch)219 internal void WriteCharEntity(char ch) { 220 if (singleStringValue != null) { 221 StartComplexValue(); 222 } 223 stringValue.Append(ch); 224 AddItem(ItemType.CharEntity, ch); 225 } 226 WriteSurrogateCharEntity(char lowChar, char highChar)227 internal void WriteSurrogateCharEntity(char lowChar, char highChar) { 228 if (singleStringValue != null) { 229 StartComplexValue(); 230 } 231 stringValue.Append(highChar); 232 stringValue.Append(lowChar); 233 AddItem(ItemType.SurrogateCharEntity, new char[] { lowChar, highChar }); 234 } 235 WriteWhitespace(string ws)236 internal void WriteWhitespace(string ws) { 237 if (singleStringValue != null) { 238 StartComplexValue(); 239 } 240 stringValue.Append(ws); 241 AddItem(ItemType.Whitespace, ws); 242 } 243 WriteString(string text)244 internal void WriteString(string text) { 245 if (singleStringValue != null) { 246 StartComplexValue(); 247 } 248 else { 249 // special-case for a single WriteString 250 if (lastItem == -1) { 251 singleStringValue = text; 252 return; 253 } 254 } 255 256 stringValue.Append(text); 257 AddItem(ItemType.String, text); 258 } 259 WriteChars(char[] buffer, int index, int count)260 internal void WriteChars(char[] buffer, int index, int count) { 261 if (singleStringValue != null) { 262 StartComplexValue(); 263 } 264 stringValue.Append(buffer, index, count); 265 AddItem(ItemType.StringChars, new BufferChunk(buffer, index, count)); 266 } 267 WriteRaw(char[] buffer, int index, int count)268 internal void WriteRaw(char[] buffer, int index, int count) { 269 if (singleStringValue != null) { 270 StartComplexValue(); 271 } 272 stringValue.Append(buffer, index, count); 273 AddItem(ItemType.RawChars, new BufferChunk(buffer, index, count)); 274 } 275 WriteRaw(string data)276 internal void WriteRaw(string data) { 277 if (singleStringValue != null) { 278 StartComplexValue(); 279 } 280 stringValue.Append(data); 281 AddItem(ItemType.Raw, data); 282 } 283 WriteValue(string value)284 internal void WriteValue(string value) { 285 if (singleStringValue != null) { 286 StartComplexValue(); 287 } 288 stringValue.Append(value); 289 AddItem(ItemType.ValueString, value); 290 } 291 Replay(XmlWriter writer)292 internal void Replay(XmlWriter writer) { 293 if (singleStringValue != null) { 294 writer.WriteString(singleStringValue); 295 return; 296 } 297 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; 335 } 336 } 337 } 338 339 // This method trims whitespaces from the beginnig and the end of the string and cached writer events Trim()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; 345 } 346 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); 352 } 353 354 // trim the beginning of the recorded writer events 355 XmlCharType xmlCharType = XmlCharType.Instance; 356 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++; 371 } 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--; 380 } 381 if (bufChunk.index == endIndex) { 382 // no characters left -> move the firstItem index to exclude it from the Replay 383 firstItem++; 384 } 385 break; 386 } 387 i++; 388 } 389 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--; 405 } 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--; 412 } 413 if (bufChunk.count == 0) { 414 // no characters left -> move the lastItem index to exclude it from the Replay 415 lastItem--; 416 } 417 break; 418 } 419 i--; 420 } 421 } 422 Clear()423 internal void Clear() { 424 singleStringValue = null; 425 lastItem = -1; 426 firstItem = 0; 427 stringValue.Length = 0; 428 } 429 StartComplexValue()430 private void StartComplexValue() { 431 Debug.Assert(singleStringValue != null); 432 Debug.Assert(lastItem == -1); 433 434 stringValue.Append( singleStringValue ); 435 AddItem(ItemType.String, singleStringValue); 436 437 singleStringValue = null; 438 } 439 AddItem(ItemType type, object data)440 void AddItem(ItemType type, object data) { 441 int newItemIndex = lastItem + 1; 442 if (items == null) { 443 items = new Item[4]; 444 } 445 else if (items.Length == newItemIndex) { 446 Item[] newItems = new Item[newItemIndex * 2]; 447 Array.Copy(items, newItems, newItemIndex); 448 items = newItems; 449 } 450 if (items[newItemIndex] == null) { 451 items[newItemIndex] = new Item(); 452 } 453 items[newItemIndex].Set(type, data); 454 lastItem = newItemIndex; 455 } 456 457 } 458 } 459 } 460