2006-07-27 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / Mono.Xml.XPath / DTMXPathDocumentWriter2.cs
1 //
2 // Mono.Xml.XPath.DTMXPathDocumentWriter2
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // (C) 2004 Novell Inc.
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Collections;
32 using System.IO;
33 using System.Xml;
34 using System.Xml.Schema;
35 using System.Xml.XPath;
36
37 namespace Mono.Xml.XPath
38 {
39 #if OUTSIDE_SYSTEM_XML
40         public
41 #else
42         internal
43 #endif
44                 class DTMXPathDocumentWriter2 : XmlWriter
45         {
46                 public DTMXPathDocumentWriter2 (XmlNameTable nt, int defaultCapacity)
47                 {
48                         nameTable = nt == null ? new NameTable () : nt;
49                         nodeCapacity = defaultCapacity;
50                         attributeCapacity = nodeCapacity;
51                         nsCapacity = 10;
52                         idTable = new Hashtable ();
53
54                         nodes = new DTMXPathLinkedNode2 [nodeCapacity];
55                         attributes = new DTMXPathAttributeNode2 [attributeCapacity];
56                         namespaces = new DTMXPathNamespaceNode2 [nsCapacity];
57
58                         atomicStringPool = new string [20];
59                         nonAtomicStringPool = new string [20];
60
61                         Init ();
62                 }
63                 
64                 XmlNameTable nameTable;
65                 int nodeCapacity;
66                 int attributeCapacity;
67                 int nsCapacity;
68
69                 // Linked Node
70                 DTMXPathLinkedNode2 [] nodes;
71
72                 // Attribute
73                 DTMXPathAttributeNode2 [] attributes;
74
75                 // NamespaceNode
76                 DTMXPathNamespaceNode2 [] namespaces;
77
78                 // String pool
79                 string [] atomicStringPool;
80                 int atomicIndex;
81                 string [] nonAtomicStringPool;
82                 int nonAtomicIndex;
83
84                 // idTable [string value] -> int nodeId
85                 Hashtable idTable;
86
87                 int nodeIndex;
88                 int attributeIndex;
89                 int nsIndex;
90                 int [] parentStack = new int [10];
91                 int parentStackIndex = 0;
92
93                 // for attribute processing; should be reset per each element.
94                 bool hasAttributes;
95                 bool hasLocalNs;
96                 int attrIndexAtStart;
97                 int nsIndexAtStart;
98
99                 int lastNsInScope;
100
101                 // They are only used in Writer
102                 int prevSibling;
103                 WriteState state;
104                 bool openNamespace;
105                 bool isClosed;
106
107                 public DTMXPathDocument2 CreateDocument ()
108                 {
109                         if (!isClosed)
110                                 Close ();
111                         return new DTMXPathDocument2 (nameTable,
112                                 nodes,
113                                 attributes,
114                                 namespaces,
115                                 atomicStringPool,
116                                 nonAtomicStringPool,
117                                 idTable
118                         );
119                 }
120
121                 public void Init ()
122                 {
123                         // string pool index 0 to 3 are fixed.
124                         atomicStringPool [0] = nonAtomicStringPool [0] = "";
125                         atomicStringPool [1] = nonAtomicStringPool [1] = null;
126                         atomicStringPool [2] = nonAtomicStringPool [2] = XmlNamespaces.XML;
127                         atomicStringPool [3] = nonAtomicStringPool [3] = XmlNamespaces.XMLNS;
128                         atomicIndex = nonAtomicIndex = 4;
129
130                         // index 0 is dummy. No node (including Root) is assigned to this index
131                         // So that we can easily compare index != 0 instead of index < 0.
132                         // (Difference between jnz or jbe in 80x86.)
133                         AddNode (0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
134                         nodeIndex++;
135                         AddAttribute (0, "", "", "", "", 0, 0);
136                         AddNsNode (0, "", "", 0);
137                         nsIndex++;
138                         AddNsNode (1, "xml", XmlNamespaces.XML, 0);
139
140                         // add root.
141                         AddNode (0, 0, 0, XPathNodeType.Root, "", false, "", "", "", "", "", 1, 0, 0);
142
143                         this.nodeIndex = 1;
144                         this.lastNsInScope = 1;
145                         parentStack [0] = nodeIndex;
146
147                         state = WriteState.Content;
148                 }
149
150                 private int GetParentIndex ()
151                 {
152                         return parentStack [parentStackIndex];
153                 }
154
155                 private int GetPreviousSiblingIndex ()
156                 {
157                         int parent = parentStack [parentStackIndex];
158                         if (parent == nodeIndex)
159                                 return 0;
160                         int prevSibling = nodeIndex;
161                         while (nodes [prevSibling].Parent != parent)
162                                 prevSibling = nodes [prevSibling].Parent;
163                         return prevSibling;
164                 }
165
166                 private void UpdateTreeForAddition ()
167                 {
168                         int parent = GetParentIndex ();
169                         prevSibling = GetPreviousSiblingIndex ();
170
171                         nodeIndex++;
172
173                         if (prevSibling != 0)
174                                 nodes [prevSibling].NextSibling = nodeIndex;
175                         if (parent == nodeIndex - 1)
176                                 nodes [parent].FirstChild = nodeIndex;
177                 }
178
179                 private void CloseStartElement ()
180                 {
181                         if (attrIndexAtStart != attributeIndex)
182                                 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
183                         if (nsIndexAtStart != nsIndex) {
184                                 nodes [nodeIndex].FirstNamespace = nsIndex;
185                                 lastNsInScope = nsIndex;
186                         }
187
188                         parentStackIndex++;
189                         if (parentStack.Length == parentStackIndex) {
190                                 int [] tmp = new int [parentStackIndex * 2];
191                                 Array.Copy (parentStack, tmp, parentStackIndex);
192                                 parentStack = tmp;
193                         }
194                         parentStack [parentStackIndex] = nodeIndex;
195 \r
196                         state = WriteState.Content;\r
197                 }
198
199                 #region Adding Nodes
200
201                 private int AtomicIndex (string s)
202                 {
203                         if (s == "")
204                                 return 0;
205                         if (s == null)
206                                 return 1;
207                         int i = 2;
208                         for (; i < atomicIndex; i++)
209                                 if (Object.ReferenceEquals (s, atomicStringPool [i]))
210                                         return i;
211
212                         if (atomicIndex == atomicStringPool.Length) {
213                                 string [] newArr = new string [atomicIndex * 2];
214                                 Array.Copy (atomicStringPool, newArr, atomicIndex);
215                                 atomicStringPool = newArr;
216                         }
217                         atomicStringPool [atomicIndex] = s;
218                         return atomicIndex++;
219                 }
220
221                 private int NonAtomicIndex (string s)
222                 {
223                         if (s == "")
224                                 return 0;
225                         if (s == null)
226                                 return 1;
227                         int i = 2;
228
229                         // Here we don't compare all the entries (sometimes it
230                         // goes extremely slow).
231                         int max = nonAtomicIndex < 100 ? nonAtomicIndex : 100;
232                         for (; i < max; i++)
233                                 if (s == nonAtomicStringPool [i])
234                                         return i;
235
236                         if (nonAtomicIndex == nonAtomicStringPool.Length) {
237                                 string [] newArr = new string [nonAtomicIndex * 2];
238                                 Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
239                                 nonAtomicStringPool = newArr;
240                         }
241                         nonAtomicStringPool [nonAtomicIndex] = s;
242                         return nonAtomicIndex++;
243                 }
244
245                 private void SetNodeArrayLength (int size)
246                 {
247                         DTMXPathLinkedNode2 [] newArr = new DTMXPathLinkedNode2 [size];
248                         Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
249                         nodes = newArr;
250                 }
251
252                 private void SetAttributeArrayLength (int size)
253                 {
254                         DTMXPathAttributeNode2 [] newArr = 
255                                 new DTMXPathAttributeNode2 [size];
256                         Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
257                         attributes = newArr;
258                 }
259
260                 private void SetNsArrayLength (int size)
261                 {
262                         DTMXPathNamespaceNode2 [] newArr =
263                                 new DTMXPathNamespaceNode2 [size];
264                         Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
265                         namespaces = newArr;
266                 }
267
268                 // Here followings are skipped: firstChild, nextSibling, 
269                 public void AddNode (int parent, int firstAttribute, int previousSibling, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
270                 {
271                         if (nodes.Length < nodeIndex + 1) {
272                                 nodeCapacity *= 4;
273                                 SetNodeArrayLength (nodeCapacity);
274                         }
275
276 #if DTM_CLASS
277                         nodes [nodeIndex] = new DTMXPathLinkedNode2 ();
278 #endif
279                         nodes [nodeIndex].FirstChild = 0;               // dummy
280                         nodes [nodeIndex].Parent = parent;
281                         nodes [nodeIndex].FirstAttribute = firstAttribute;
282                         nodes [nodeIndex].PreviousSibling = previousSibling;
283                         nodes [nodeIndex].NextSibling = 0;      // dummy
284                         nodes [nodeIndex].NodeType = nodeType;
285                         nodes [nodeIndex].BaseURI = AtomicIndex (baseUri);
286                         nodes [nodeIndex].IsEmptyElement = isEmptyElement;
287                         nodes [nodeIndex].LocalName = AtomicIndex (localName);
288                         nodes [nodeIndex].NamespaceURI = AtomicIndex (ns);
289                         nodes [nodeIndex].Prefix = AtomicIndex (prefix);
290                         nodes [nodeIndex].Value = NonAtomicIndex (value);
291                         nodes [nodeIndex].XmlLang = AtomicIndex (xmlLang);
292                         nodes [nodeIndex].FirstNamespace = namespaceNode;
293                         nodes [nodeIndex].LineNumber = lineNumber;
294                         nodes [nodeIndex].LinePosition = linePosition;
295                 }
296
297                 // Followings are skipped: nextAttribute,
298                 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, int lineNumber, int linePosition)
299                 {
300                         if (attributes.Length < attributeIndex + 1) {
301                                 attributeCapacity *= 4;
302                                 SetAttributeArrayLength (attributeCapacity);
303                         }
304
305 #if DTM_CLASS
306                         attributes [attributeIndex] = new DTMXPathAttributeNode2 ();
307 #endif
308                         attributes [attributeIndex].OwnerElement = ownerElement;
309                         attributes [attributeIndex].LocalName = AtomicIndex (localName);
310                         attributes [attributeIndex].NamespaceURI = AtomicIndex (ns);
311                         attributes [attributeIndex].Prefix = AtomicIndex (prefix);
312                         attributes [attributeIndex].Value = NonAtomicIndex (value);
313                         attributes [attributeIndex].LineNumber = lineNumber;
314                         attributes [attributeIndex].LinePosition = linePosition;
315                 }
316
317                 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
318                 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
319                 {
320                         if (namespaces.Length < nsIndex + 1) {
321                                 nsCapacity *= 4;
322                                 SetNsArrayLength (nsCapacity);
323                         }
324
325 #if DTM_CLASS
326                         namespaces [nsIndex] = new DTMXPathNamespaceNode2 ();
327 #endif
328                         namespaces [nsIndex].DeclaredElement = declaredElement;
329                         namespaces [nsIndex].Name = AtomicIndex (name);
330                         namespaces [nsIndex].Namespace = AtomicIndex (ns);
331                         namespaces [nsIndex].NextNamespace = nextNs;
332                 }
333                 #endregion
334
335                 #region XmlWriter methods
336                 // They are not supported
337                 public override string XmlLang { get { return null; } }
338                 public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
339
340                 public override WriteState WriteState { get { return state; } }
341
342                 public override void Close ()
343                 {
344                         // Fixup arrays
345                         SetNodeArrayLength (nodeIndex + 1);
346                         SetAttributeArrayLength (attributeIndex + 1);
347                         SetNsArrayLength (nsIndex + 1);
348
349                         string [] newArr = new string [atomicIndex];
350                         Array.Copy (atomicStringPool, newArr, atomicIndex);
351                         atomicStringPool = newArr;
352
353                         newArr = new string [nonAtomicIndex];
354                         Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
355                         nonAtomicStringPool = newArr;
356
357                         isClosed = true;
358                 }
359
360                 public override void Flush ()
361                 {
362                         // do nothing
363                 }
364
365                 public override string LookupPrefix (string ns)
366                 {
367                         int tmp = nsIndex;
368                         while (tmp != 0) {
369                                 if (atomicStringPool [namespaces [tmp].Namespace] == ns)
370                                         return atomicStringPool [namespaces [tmp].Name];
371                                 tmp = namespaces [tmp].NextNamespace;
372                         }
373                         return null;
374                 }
375 \r
376                 public override void WriteCData (string data)\r
377                 {\r
378                         AddTextNode (data);\r
379                 }\r
380 \r
381                 private void AddTextNode (string data)\r
382                 {\r
383                         switch (state) {\r
384                         case WriteState.Element:\r
385                                 CloseStartElement ();\r
386                                 break;\r
387                         case WriteState.Content:\r
388                                 break;\r
389                         default:\r
390                                 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);\r
391                         }\r
392 \r
393                         // When text after text, just add the value, and return.\r
394                         if (nodes [nodeIndex].Parent == parentStack [parentStackIndex]) {
395                                 switch (nodes [nodeIndex].NodeType) {\r
396                                 case XPathNodeType.Text:\r
397                                 case XPathNodeType.SignificantWhitespace:
398                                         string value = nonAtomicStringPool [nodes [nodeIndex].Value] + data;
399                                         nodes [nodeIndex].Value = NonAtomicIndex (value);\r
400                                         if (IsWhitespace (value))\r
401                                                 nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;\r
402                                         else\r
403                                                 nodes [nodeIndex].NodeType = XPathNodeType.Text;\r
404                                         return;\r
405                                 }\r
406                         }\r
407 \r
408                         int parent = GetParentIndex ();\r
409                         UpdateTreeForAddition ();\r
410
411                         AddNode (parent,
412                                 0, // attribute
413                                 prevSibling,
414                                 XPathNodeType.Text,
415                                 null,
416                                 false,
417                                 null,
418                                 String.Empty,
419                                 String.Empty,
420                                 data,
421                                 null,
422                                 0, // nsIndex
423                                 0, // line info
424                                 0);
425                 }\r
426 \r
427                 private void CheckTopLevelNode ()\r
428                 {\r
429                         switch (state) {\r
430                         case WriteState.Element:\r
431                                 CloseStartElement ();\r
432                                 break;\r
433                         case WriteState.Content:\r
434                         case WriteState.Prolog:\r
435                         case WriteState.Start:\r
436                                 break;\r
437                         default:\r
438                                 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);\r
439                         }\r
440                 }\r
441 \r
442                 public override void WriteComment (string data)\r
443                 {\r
444                         CheckTopLevelNode ();\r
445 \r
446                         int parent = GetParentIndex ();\r
447                         UpdateTreeForAddition ();\r
448
449                         AddNode (parent,
450                                 0, // attribute
451                                 prevSibling,
452                                 XPathNodeType.Comment,
453                                 null,
454                                 false,
455                                 null,
456                                 String.Empty,
457                                 String.Empty,
458                                 data,
459                                 null,
460                                 0, // nsIndex
461                                 0, // line info
462                                 0);
463                 }\r
464 \r
465                 public override void WriteProcessingInstruction (string name, string data)\r
466                 {\r
467                         CheckTopLevelNode ();\r
468 \r
469                         int parent = GetParentIndex ();\r
470                         UpdateTreeForAddition ();\r
471
472                         AddNode (parent,
473                                 0, // attribute
474                                 prevSibling,
475                                 XPathNodeType.ProcessingInstruction,
476                                 null,
477                                 false,
478                                 name,
479                                 String.Empty,
480                                 String.Empty,
481                                 data,
482                                 null,
483                                 0, // nsIndex
484                                 0, // line info
485                                 0);
486                 }\r
487 \r
488                 public override void WriteWhitespace (string data)\r
489                 {\r
490                         CheckTopLevelNode ();\r
491 \r
492                         int parent = GetParentIndex ();\r
493                         UpdateTreeForAddition ();\r
494
495                         AddNode (parent,
496                                 0, // attribute
497                                 prevSibling,
498                                 XPathNodeType.Whitespace,
499                                 null,
500                                 false,
501                                 null,
502                                 String.Empty,
503                                 String.Empty,
504                                 data,
505                                 null,
506                                 0, // nsIndex
507                                 0, // line info
508                                 0);
509                 }\r
510 \r
511                 public override void WriteStartDocument ()\r
512                 {\r
513                         // do nothing\r
514                 }\r
515 \r
516                 public override void WriteStartDocument (bool standalone)\r
517                 {\r
518                         // do nothing\r
519                 }\r
520 \r
521                 public override void WriteEndDocument ()\r
522                 {\r
523                         // do nothing\r
524                 }\r
525 \r
526                 public override void WriteStartElement (string prefix, string localName, string ns)\r
527                 {\r
528                         switch (state) {\r
529                         case WriteState.Element:\r
530                                 CloseStartElement ();\r
531                                 break;\r
532                         case WriteState.Start:\r
533                         case WriteState.Prolog:\r
534                         case WriteState.Content:\r
535                                 break;\r
536                         default:\r
537                                 throw new InvalidOperationException ("Invalid document state for writing element: " + state);\r
538                         }\r
539 \r
540                         int parent = GetParentIndex ();\r
541                         UpdateTreeForAddition ();\r
542
543                         WriteStartElement (parent, prevSibling, prefix, localName, ns);
544                         state = WriteState.Element;
545                 }\r
546
547                 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
548                 {
549                         PrepareStartElement (previousSibling);
550
551                         AddNode (parent,
552                                 0, // dummy:firstAttribute
553                                 previousSibling,
554                                 XPathNodeType.Element,
555                                 null,
556                                 false,
557                                 localName,
558                                 ns,
559                                 prefix,
560                                 "",     // Element has no internal value.
561                                 null,
562                                 lastNsInScope,
563                                 0,
564                                 0);
565                 }
566
567                 private void PrepareStartElement (int previousSibling)
568                 {
569                         hasAttributes = false;
570                         hasLocalNs = false;
571                         attrIndexAtStart = attributeIndex;
572                         nsIndexAtStart = nsIndex;
573
574                         while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
575                                 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
576                         }
577                 }
578 \r
579                 public override void WriteEndElement ()\r
580                 {\r
581                         WriteEndElement (false);\r
582                 }\r
583 \r
584                 public override void WriteFullEndElement ()\r
585                 {\r
586                         WriteEndElement (true);\r
587                 }\r
588 \r
589                 private void WriteEndElement (bool full)\r
590                 {\r
591                         switch (state) {\r
592                         case WriteState.Element:\r
593                                 CloseStartElement ();\r
594                                 break;\r
595                         case WriteState.Content:\r
596                                 break;\r
597                         default:\r
598                                 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);\r
599                         }\r
600                         parentStackIndex--;
601                         if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {\r
602                                 if (!full)\r
603                                         nodes [nodeIndex].IsEmptyElement = true;\r
604                         }
605                 }\r
606 \r
607                 public override void WriteStartAttribute (string prefix, string localName, string ns)\r
608                 {\r
609                         if (state != WriteState.Element)\r
610                                 throw new InvalidOperationException ("Invalid document state for attribute: " + state);\r
611 \r
612                         state = WriteState.Attribute;\r
613                         if (ns == XmlNamespaces.XMLNS)
614                                 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
615                         else
616                                 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
617                 }\r
618 \r
619                 private void ProcessNamespace (string prefix, string ns)
620                 {
621                         int nextTmp = hasLocalNs ? nsIndex : nodes [nodeIndex].FirstNamespace;
622
623                         nsIndex++;
624
625                         this.AddNsNode (nodeIndex,
626                                 prefix,
627                                 ns,
628                                 nextTmp);
629                         hasLocalNs = true;
630                         openNamespace = true;
631                 }
632
633                 private void ProcessAttribute (string prefix, string localName, string ns, string value)
634                 {
635                         attributeIndex ++;
636
637                         this.AddAttribute (nodeIndex,
638                                 localName,
639                                 ns, 
640                                 prefix != null ? prefix : String.Empty, 
641                                 value,
642                                 0,
643                                 0);
644                         if (hasAttributes)
645                                 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
646                         else
647                                 hasAttributes = true;
648                 }
649
650                 public override void WriteEndAttribute ()\r
651                 {\r
652                         if (state != WriteState.Attribute)\r
653                                 throw new InvalidOperationException ();\r
654 \r
655                         openNamespace = false;\r
656                         state = WriteState.Element;\r
657                 }\r
658 \r
659                 public override void WriteString (string text)\r
660                 {\r
661                         if (WriteState == WriteState.Attribute) {
662                                 if (openNamespace) {\r
663                                         string value = atomicStringPool [namespaces [nsIndex].Namespace] + text;
664                                         namespaces [nsIndex].Namespace = AtomicIndex (value);
665                                 } else {\r
666                                         string value = nonAtomicStringPool [attributes [attributeIndex].Value] + text;
667                                         attributes [attributeIndex].Value = NonAtomicIndex (value);
668                                 }\r
669                         }\r
670                         else\r
671                                 AddTextNode (text);\r
672                 }\r
673 \r
674                 // Well, they cannot be supported, but actually used to\r
675                 // disable-output-escaping = "true"\r
676                 public override void WriteRaw (string data)\r
677                 {\r
678                         WriteString (data);\r
679                 }\r
680
681                 public override void WriteRaw (char [] data, int start, int len)\r
682                 {\r
683                         WriteString (new string (data, start, len));\r
684                 }\r
685 \r
686                 public override void WriteName (string name)\r
687                 {\r
688                         WriteString (name);\r
689                 }\r
690 \r
691                 public override void WriteNmToken (string name)\r
692                 {\r
693                         WriteString (name);\r
694                 }\r
695 \r
696                 public override void WriteBase64 (byte [] buffer, int index, int count)\r
697                 {\r
698                         throw new NotSupportedException ();\r
699                 }
700
701                 public override void WriteBinHex (byte [] buffer, int index, int count)\r
702                 {\r
703                         throw new NotSupportedException ();\r
704                 }\r
705 \r
706                 public override void WriteChars (char [] buffer, int index, int count)\r
707                 {\r
708                         throw new NotSupportedException ();\r
709                 }\r
710 \r
711                 public override void WriteCharEntity (char c)\r
712                 {\r
713                         throw new NotSupportedException ();\r
714                 }\r
715 \r
716                 public override void WriteDocType (string name, string pub, string sys, string intSubset)\r
717                 {\r
718                         throw new NotSupportedException ();\r
719                 }\r
720
721                 public override void WriteEntityRef (string name)
722                 {
723                         throw new NotSupportedException ();\r
724                 }
725
726                 public override void WriteQualifiedName (string localName, string ns)\r
727                 {\r
728                         throw new NotSupportedException ();\r
729                 }\r
730 \r
731                 public override void WriteSurrogateCharEntity (char high, char low)\r
732                 {\r
733                         throw new NotSupportedException ();\r
734                 }
735
736                 private bool IsWhitespace (string data)
737                 {
738                         for (int i = 0; i < data.Length; i++) {
739                                 switch (data [i]) {
740                                 case ' ':
741                                 case '\r':
742                                 case '\n':
743                                 case '\t':
744                                         continue;
745                                 default:
746                                         return false;
747                                 }
748                         }
749                         return true;
750                 }\r
751                 #endregion
752         }
753 }