2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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                 string [] nonAtomicStringPool;
81
82                 // idTable [string value] -> int nodeId
83                 Hashtable idTable;
84
85                 int nodeIndex;
86                 int attributeIndex;
87                 int nsIndex;
88                 int parentForFirstChild;
89
90                 // for attribute processing; should be reset per each element.
91                 int firstAttributeIndex;
92                 int lastNsIndexInCurrent;
93                 int attrIndexAtStart;
94                 int nsIndexAtStart;
95
96                 int prevSibling;
97                 int lastNsInScope;
98
99                 // They are only used in Writer
100                 int writerDepth;
101                 WriteState state;
102                 bool openNamespace;
103                 bool isClosed;
104
105                 public DTMXPathDocument2 CreateDocument ()
106                 {
107                         if (!isClosed)
108                                 Close ();
109                         return new DTMXPathDocument2 (nameTable,
110                                 nodes,
111                                 attributes,
112                                 namespaces,
113                                 atomicStringPool,
114                                 nonAtomicStringPool,
115                                 idTable
116                         );
117                 }
118
119                 public void Init ()
120                 {
121                         // string pool index 0 to 3 are fixed.
122                         atomicStringPool [0] = nonAtomicStringPool [0] = "";
123                         atomicStringPool [1] = nonAtomicStringPool [1] = null;
124                         atomicStringPool [2] = nonAtomicStringPool [2] = XmlNamespaces.XML;
125                         atomicStringPool [3] = nonAtomicStringPool [3] = XmlNamespaces.XMLNS;
126
127                         // index 0 is dummy. No node (including Root) is assigned to this index
128                         // So that we can easily compare index != 0 instead of index < 0.
129                         // (Difference between jnz or jbe in 80x86.)
130                         AddNode (0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
131                         nodeIndex++;
132                         AddAttribute (0, "", "", "", "", 0, 0);
133                         AddNsNode (0, "", "", 0);
134                         nsIndex++;
135                         AddNsNode (1, "xml", XmlNamespaces.XML, 0);
136
137                         // add root.
138                         AddNode (0, 0, 0, -1, XPathNodeType.Root, "", false, "", "", "", "", "", 1, 0, 0);
139
140                         this.nodeIndex = 1;
141                         this.lastNsInScope = 1;
142                         this.parentForFirstChild = nodeIndex;
143
144                         state = WriteState.Content;
145                 }
146
147                 private int GetParentIndex ()
148                 {
149                         if (parentForFirstChild >= 0)
150                                 return parentForFirstChild;
151
152                         int parent = nodeIndex;
153                         if (nodes [nodeIndex].Depth >= writerDepth) {
154                                 // if not, then current node is parent.
155                                 while (writerDepth <= nodes [parent].Depth)
156                                         parent = nodes [parent].Parent;
157                         }
158                         return parent;
159                 }
160
161                 private int GetPreviousSiblingIndex ()
162                 {
163                         int prevSibling = nodeIndex;
164                         if (parentForFirstChild >= 0)
165                                 prevSibling = 0;
166                         else
167                                 while (nodes [prevSibling].Depth != writerDepth)
168                                         prevSibling = nodes [prevSibling].Parent;
169                         return prevSibling;
170                 }
171
172                 private void UpdateTreeForAddition ()
173                 {
174                         int parent = GetParentIndex ();
175                         prevSibling = GetPreviousSiblingIndex ();
176
177                         nodeIndex++;
178
179                         if (prevSibling != 0)
180                                 nodes [prevSibling].NextSibling = nodeIndex;
181                         if (parentForFirstChild >= 0)
182                                 nodes [parent].FirstChild = nodeIndex;
183
184                         parentForFirstChild = -1;
185                 }
186
187                 private void CloseStartElement ()
188                 {
189                         if (attrIndexAtStart != attributeIndex)
190                                 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
191                         if (nsIndexAtStart != nsIndex) {
192                                 nodes [nodeIndex].FirstNamespace = nsIndex;
193                                 lastNsInScope = nsIndex;
194                         }
195
196                         if (!nodes [nodeIndex].IsEmptyElement)
197                                 parentForFirstChild = nodeIndex;
198 \r
199                         state = WriteState.Content;\r
200                         writerDepth++;
201                 }
202
203                 #region Adding Nodes
204
205                 private int AtomicIndex (string s)
206                 {
207                         if (s == "")
208                                 return 0;
209                         if (s == null)
210                                 return 1;
211                         int i = 2;
212                         for (; i < atomicStringPool.Length; i++) {
213                                 if (atomicStringPool [i] == null) {
214                                         atomicStringPool [i] = s;
215                                         return i;
216                                 }
217                                 else if (Object.ReferenceEquals (s,
218                                         atomicStringPool [i]))
219                                         return i;
220                         }
221                         string [] newArr = new string [i * 2];
222                         Array.Copy (atomicStringPool, newArr, i);
223                         atomicStringPool = newArr;
224                         atomicStringPool [i] = s;
225                         return i;
226                 }
227
228                 private int NonAtomicIndex (string s)
229                 {
230                         if (s == "")
231                                 return 0;
232                         if (s == null)
233                                 return 1;
234                         int i = 2;
235                         for (; i < nonAtomicStringPool.Length; i++) {
236                                 if (nonAtomicStringPool [i] == null) {
237                                         nonAtomicStringPool [i] = s;
238                                         return i;
239                                 }
240                                 else if (s == nonAtomicStringPool [i])
241                                         return i;
242                         }
243                         string [] newArr = new string [i * 2];
244                         Array.Copy (nonAtomicStringPool, newArr, i);
245                         nonAtomicStringPool = newArr;
246                         nonAtomicStringPool [i] = s;
247                         return i;
248                 }
249
250                 private void SetNodeArrayLength (int size)
251                 {
252                         DTMXPathLinkedNode2 [] newArr = new DTMXPathLinkedNode2 [size];
253                         Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
254                         nodes = newArr;
255                 }
256
257                 private void SetAttributeArrayLength (int size)
258                 {
259                         DTMXPathAttributeNode2 [] newArr = 
260                                 new DTMXPathAttributeNode2 [size];
261                         Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
262                         attributes = newArr;
263                 }
264
265                 private void SetNsArrayLength (int size)
266                 {
267                         DTMXPathNamespaceNode2 [] newArr =
268                                 new DTMXPathNamespaceNode2 [size];
269                         Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
270                         namespaces = newArr;
271                 }
272
273                 // Here followings are skipped: firstChild, nextSibling, 
274                 public void AddNode (int parent, int firstAttribute, int previousSibling, int depth, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
275                 {
276                         if (nodes.Length < nodeIndex + 1) {
277                                 nodeCapacity *= 4;
278                                 SetNodeArrayLength (nodeCapacity);
279                         }
280
281 #if DTM_CLASS
282                         nodes [nodeIndex] = new DTMXPathLinkedNode2 ();
283 #endif
284                         nodes [nodeIndex].FirstChild = 0;               // dummy
285                         nodes [nodeIndex].Parent = parent;
286                         nodes [nodeIndex].FirstAttribute = firstAttribute;
287                         nodes [nodeIndex].PreviousSibling = previousSibling;
288                         nodes [nodeIndex].NextSibling = 0;      // dummy
289                         nodes [nodeIndex].Depth = depth;
290                         nodes [nodeIndex].NodeType = nodeType;
291                         nodes [nodeIndex].BaseURI = AtomicIndex (baseUri);
292                         nodes [nodeIndex].IsEmptyElement = isEmptyElement;
293                         nodes [nodeIndex].LocalName = AtomicIndex (localName);
294                         nodes [nodeIndex].NamespaceURI = AtomicIndex (ns);
295                         nodes [nodeIndex].Prefix = AtomicIndex (prefix);
296                         nodes [nodeIndex].Value = NonAtomicIndex (value);
297                         nodes [nodeIndex].XmlLang = AtomicIndex (xmlLang);
298                         nodes [nodeIndex].FirstNamespace = namespaceNode;
299                         nodes [nodeIndex].LineNumber = lineNumber;
300                         nodes [nodeIndex].LinePosition = linePosition;
301                 }
302
303                 // Followings are skipped: nextAttribute,
304                 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, int lineNumber, int linePosition)
305                 {
306                         if (attributes.Length < attributeIndex + 1) {
307                                 attributeCapacity *= 4;
308                                 SetAttributeArrayLength (attributeCapacity);
309                         }
310
311 #if DTM_CLASS
312                         attributes [attributeIndex] = new DTMXPathAttributeNode2 ();
313 #endif
314                         attributes [attributeIndex].OwnerElement = ownerElement;
315                         attributes [attributeIndex].LocalName = AtomicIndex (localName);
316                         attributes [attributeIndex].NamespaceURI = AtomicIndex (ns);
317                         attributes [attributeIndex].Prefix = AtomicIndex (prefix);
318                         attributes [attributeIndex].Value = NonAtomicIndex (value);
319                         attributes [attributeIndex].LineNumber = lineNumber;
320                         attributes [attributeIndex].LinePosition = linePosition;
321                 }
322
323                 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
324                 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
325                 {
326                         if (namespaces.Length < nsIndex + 1) {
327                                 nsCapacity *= 4;
328                                 SetNsArrayLength (nsCapacity);
329                         }
330
331 #if DTM_CLASS
332                         namespaces [nsIndex] = new DTMXPathNamespaceNode2 ();
333 #endif
334                         namespaces [nsIndex].DeclaredElement = declaredElement;
335                         namespaces [nsIndex].Name = AtomicIndex (name);
336                         namespaces [nsIndex].Namespace = AtomicIndex (ns);
337                         namespaces [nsIndex].NextNamespace = nextNs;
338                 }
339                 #endregion
340
341                 #region XmlWriter methods
342                 // They are not supported
343                 public override string XmlLang { get { return null; } }
344                 public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
345
346                 public override WriteState WriteState { get { return state; } }
347
348                 public override void Close ()
349                 {
350                         // Fixup arrays
351                         SetNodeArrayLength (nodeIndex + 1);
352                         SetAttributeArrayLength (attributeIndex + 1);
353                         SetNsArrayLength (nsIndex + 1);
354                         isClosed = true;
355                 }
356
357                 public override void Flush ()
358                 {
359                         // do nothing
360                 }
361
362                 public override string LookupPrefix (string ns)
363                 {
364                         int tmp = nsIndex;
365                         while (tmp != 0) {
366                                 if (atomicStringPool [namespaces [tmp].Namespace] == ns)
367                                         return atomicStringPool [namespaces [tmp].Name];
368                                 tmp = namespaces [tmp].NextNamespace;
369                         }
370                         return null;
371                 }
372 \r
373                 public override void WriteCData (string data)\r
374                 {\r
375                         AddTextNode (data);\r
376                 }\r
377 \r
378                 private void AddTextNode (string data)\r
379                 {\r
380                         switch (state) {\r
381                         case WriteState.Element:\r
382                                 CloseStartElement ();\r
383                                 break;\r
384                         case WriteState.Content:\r
385                                 break;\r
386                         default:\r
387                                 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);\r
388                         }\r
389 \r
390                         // When text after text, just add the value, and return.\r
391                         if (nodes [nodeIndex].Depth == writerDepth) {\r
392                                 switch (nodes [nodeIndex].NodeType) {\r
393                                 case XPathNodeType.Text:\r
394                                 case XPathNodeType.SignificantWhitespace:
395                                         string value = nonAtomicStringPool [nodes [nodeIndex].Value] + data;
396                                         nodes [nodeIndex].Value = NonAtomicIndex (value);\r
397                                         if (IsWhitespace (value))\r
398                                                 nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;\r
399                                         else\r
400                                                 nodes [nodeIndex].NodeType = XPathNodeType.Text;\r
401                                         return;\r
402                                 }\r
403                         }\r
404 \r
405                         int parent = GetParentIndex ();\r
406                         UpdateTreeForAddition ();\r
407
408                         AddNode (parent,
409                                 0, // attribute
410                                 prevSibling,
411                                 writerDepth,
412                                 XPathNodeType.Text,
413                                 null,
414                                 false,
415                                 null,
416                                 String.Empty,
417                                 String.Empty,
418                                 data,
419                                 null,
420                                 0, // nsIndex
421                                 0, // line info
422                                 0);
423                 }\r
424 \r
425                 private void CheckTopLevelNode ()\r
426                 {\r
427                         switch (state) {\r
428                         case WriteState.Element:\r
429                                 CloseStartElement ();\r
430                                 break;\r
431                         case WriteState.Content:\r
432                         case WriteState.Prolog:\r
433                         case WriteState.Start:\r
434                                 break;\r
435                         default:\r
436                                 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);\r
437                         }\r
438                 }\r
439 \r
440                 public override void WriteComment (string data)\r
441                 {\r
442                         CheckTopLevelNode ();\r
443 \r
444                         int parent = GetParentIndex ();\r
445                         UpdateTreeForAddition ();\r
446
447                         AddNode (parent,
448                                 0, // attribute
449                                 prevSibling,
450                                 writerDepth,
451                                 XPathNodeType.Comment,
452                                 null,
453                                 false,
454                                 null,
455                                 String.Empty,
456                                 String.Empty,
457                                 data,
458                                 null,
459                                 0, // nsIndex
460                                 0, // line info
461                                 0);
462                 }\r
463 \r
464                 public override void WriteProcessingInstruction (string name, string data)\r
465                 {\r
466                         CheckTopLevelNode ();\r
467 \r
468                         int parent = GetParentIndex ();\r
469                         UpdateTreeForAddition ();\r
470
471                         AddNode (parent,
472                                 0, // attribute
473                                 prevSibling,
474                                 writerDepth,
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                                 writerDepth,
499                                 XPathNodeType.Whitespace,
500                                 null,
501                                 false,
502                                 null,
503                                 String.Empty,
504                                 String.Empty,
505                                 data,
506                                 null,
507                                 0, // nsIndex
508                                 0, // line info
509                                 0);
510                 }\r
511 \r
512                 public override void WriteStartDocument ()\r
513                 {\r
514                         // do nothing\r
515                 }\r
516 \r
517                 public override void WriteStartDocument (bool standalone)\r
518                 {\r
519                         // do nothing\r
520                 }\r
521 \r
522                 public override void WriteEndDocument ()\r
523                 {\r
524                         // do nothing\r
525                 }\r
526 \r
527                 public override void WriteStartElement (string prefix, string localName, string ns)\r
528                 {\r
529                         switch (state) {\r
530                         case WriteState.Element:\r
531                                 CloseStartElement ();\r
532                                 break;\r
533                         case WriteState.Start:\r
534                         case WriteState.Prolog:\r
535                         case WriteState.Content:\r
536                                 break;\r
537                         default:\r
538                                 throw new InvalidOperationException ("Invalid document state for writing element: " + state);\r
539                         }\r
540 \r
541                         int parent = GetParentIndex ();\r
542                         UpdateTreeForAddition ();\r
543
544                         WriteStartElement (parent, prevSibling, prefix, localName, ns);
545                         state = WriteState.Element;
546                 }\r
547
548                 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
549                 {
550                         PrepareStartElement (previousSibling);
551
552                         AddNode (parent,
553                                 0, // dummy:firstAttribute
554                                 previousSibling,
555                                 writerDepth,
556                                 XPathNodeType.Element,
557                                 null,
558                                 false,
559                                 localName,
560                                 ns,
561                                 prefix,
562                                 "",     // Element has no internal value.
563                                 null,
564                                 lastNsInScope,
565                                 0,
566                                 0);
567                 }
568
569                 private void PrepareStartElement (int previousSibling)
570                 {
571                         firstAttributeIndex = 0;
572                         lastNsIndexInCurrent = 0;
573                         attrIndexAtStart = attributeIndex;
574                         nsIndexAtStart = nsIndex;
575
576                         while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
577                                 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
578                         }
579                 }
580 \r
581                 public override void WriteEndElement ()\r
582                 {\r
583                         WriteEndElement (false);\r
584                 }\r
585 \r
586                 public override void WriteFullEndElement ()\r
587                 {\r
588                         WriteEndElement (true);\r
589                 }\r
590 \r
591                 private void WriteEndElement (bool full)\r
592                 {\r
593                         switch (state) {\r
594                         case WriteState.Element:\r
595                                 CloseStartElement ();\r
596                                 break;\r
597                         case WriteState.Content:\r
598                                 break;\r
599                         default:\r
600                                 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);\r
601                         }\r
602                         parentForFirstChild = -1;\r
603                         if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {\r
604                                 if (!full)\r
605                                         nodes [nodeIndex].IsEmptyElement = true;\r
606                         }\r
607 \r
608                         writerDepth--;\r
609                 }\r
610 \r
611                 public override void WriteStartAttribute (string prefix, string localName, string ns)\r
612                 {\r
613                         if (state != WriteState.Element)\r
614                                 throw new InvalidOperationException ("Invalid document state for attribute: " + state);\r
615 \r
616                         state = WriteState.Attribute;\r
617                         if (ns == XmlNamespaces.XMLNS)
618                                 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
619                         else
620                                 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
621                 }\r
622 \r
623                 private void ProcessNamespace (string prefix, string ns)
624                 {
625                         nsIndex++;
626
627                         int nextTmp = lastNsIndexInCurrent == 0 ? nodes [nodeIndex].FirstNamespace : lastNsIndexInCurrent;
628
629                         this.AddNsNode (nodeIndex,
630                                 prefix,
631                                 ns,
632                                 nextTmp);
633                         lastNsIndexInCurrent = nsIndex;
634                         openNamespace = true;
635                 }
636
637                 private void ProcessAttribute (string prefix, string localName, string ns, string value)
638                 {
639                         attributeIndex ++;
640
641                         this.AddAttribute (nodeIndex,
642                                 localName,
643                                 ns, 
644                                 prefix != null ? prefix : String.Empty, 
645                                 value,
646                                 0,
647                                 0);
648                         if (firstAttributeIndex == 0)
649                                 firstAttributeIndex = attributeIndex;
650                         else
651                                 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
652                 }
653
654                 public override void WriteEndAttribute ()\r
655                 {\r
656                         if (state != WriteState.Attribute)\r
657                                 throw new InvalidOperationException ();\r
658 \r
659                         openNamespace = false;\r
660                         state = WriteState.Element;\r
661                 }\r
662 \r
663                 public override void WriteString (string text)\r
664                 {\r
665                         if (WriteState == WriteState.Attribute) {
666                                 if (openNamespace) {\r
667                                         string value = atomicStringPool [namespaces [nsIndex].Namespace] + text;
668                                         namespaces [nsIndex].Namespace = AtomicIndex (value);
669                                 } else {\r
670                                         string value = nonAtomicStringPool [attributes [attributeIndex].Value] + text;
671                                         attributes [attributeIndex].Value = NonAtomicIndex (value);
672                                 }\r
673                         }\r
674                         else\r
675                                 AddTextNode (text);\r
676                 }\r
677 \r
678                 // Well, they cannot be supported, but actually used to\r
679                 // disable-output-escaping = "true"\r
680                 public override void WriteRaw (string data)\r
681                 {\r
682                         WriteString (data);\r
683                 }\r
684
685                 public override void WriteRaw (char [] data, int start, int len)\r
686                 {\r
687                         WriteString (new string (data, start, len));\r
688                 }\r
689 \r
690                 public override void WriteName (string name)\r
691                 {\r
692                         WriteString (name);\r
693                 }\r
694 \r
695                 public override void WriteNmToken (string name)\r
696                 {\r
697                         WriteString (name);\r
698                 }\r
699 \r
700                 public override void WriteBase64 (byte [] buffer, int index, int count)\r
701                 {\r
702                         throw new NotSupportedException ();\r
703                 }
704
705                 public override void WriteBinHex (byte [] buffer, int index, int count)\r
706                 {\r
707                         throw new NotSupportedException ();\r
708                 }\r
709 \r
710                 public override void WriteChars (char [] buffer, int index, int count)\r
711                 {\r
712                         throw new NotSupportedException ();\r
713                 }\r
714 \r
715                 public override void WriteCharEntity (char c)\r
716                 {\r
717                         throw new NotSupportedException ();\r
718                 }\r
719 \r
720                 public override void WriteDocType (string name, string pub, string sys, string intSubset)\r
721                 {\r
722                         throw new NotSupportedException ();\r
723                 }\r
724
725                 public override void WriteEntityRef (string name)
726                 {
727                         throw new NotSupportedException ();\r
728                 }
729
730                 public override void WriteQualifiedName (string localName, string ns)\r
731                 {\r
732                         throw new NotSupportedException ();\r
733                 }\r
734 \r
735                 public override void WriteSurrogateCharEntity (char high, char low)\r
736                 {\r
737                         throw new NotSupportedException ();\r
738                 }
739
740                 private bool IsWhitespace (string data)
741                 {
742                         for (int i = 0; i < data.Length; i++) {
743                                 switch (data [i]) {
744                                 case ' ':
745                                 case '\r':
746                                 case '\n':
747                                 case '\t':
748                                         continue;
749                                 default:
750                                         return false;
751                                 }
752                         }
753                         return true;
754                 }\r
755                 #endregion
756         }
757 }