2009-11-17 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                                 String.Empty,
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                                 String.Empty,
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                                 String.Empty,
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                 {
528                         if (ns == null)
529                                 ns = String.Empty;
530                         else if (prefix == null && ns.Length > 0)
531                                 prefix = LookupPrefix (ns);
532                         if (prefix == null)
533                                 prefix = String.Empty;
534
535                         switch (state) {\r
536                         case WriteState.Element:\r
537                                 CloseStartElement ();\r
538                                 break;\r
539                         case WriteState.Start:\r
540                         case WriteState.Prolog:\r
541                         case WriteState.Content:\r
542                                 break;\r
543                         default:\r
544                                 throw new InvalidOperationException ("Invalid document state for writing element: " + state);\r
545                         }\r
546 \r
547                         int parent = GetParentIndex ();\r
548                         UpdateTreeForAddition ();\r
549
550                         WriteStartElement (parent, prevSibling, prefix, localName, ns);
551                         state = WriteState.Element;
552                 }\r
553
554                 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
555                 {
556                         PrepareStartElement (previousSibling);
557
558                         AddNode (parent,
559                                 0, // dummy:firstAttribute
560                                 previousSibling,
561                                 XPathNodeType.Element,
562                                 null,
563                                 false,
564                                 localName,
565                                 ns,
566                                 prefix,
567                                 "",     // Element has no internal value.
568                                 null,
569                                 lastNsInScope,
570                                 0,
571                                 0);
572                 }
573
574                 private void PrepareStartElement (int previousSibling)
575                 {
576                         hasAttributes = false;
577                         hasLocalNs = false;
578                         attrIndexAtStart = attributeIndex;
579                         nsIndexAtStart = nsIndex;
580
581                         while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
582                                 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
583                         }
584                 }
585 \r
586                 public override void WriteEndElement ()\r
587                 {\r
588                         WriteEndElement (false);\r
589                 }\r
590 \r
591                 public override void WriteFullEndElement ()\r
592                 {\r
593                         WriteEndElement (true);\r
594                 }\r
595 \r
596                 private void WriteEndElement (bool full)\r
597                 {\r
598                         switch (state) {\r
599                         case WriteState.Element:\r
600                                 CloseStartElement ();\r
601                                 break;\r
602                         case WriteState.Content:\r
603                                 break;\r
604                         default:\r
605                                 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);\r
606                         }\r
607                         parentStackIndex--;
608                         if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {\r
609                                 if (!full)\r
610                                         nodes [nodeIndex].IsEmptyElement = true;\r
611                         }
612                 }\r
613 \r
614                 public override void WriteStartAttribute (string prefix, string localName, string ns)\r
615                 {\r
616                         if (ns == null)
617                                 ns = String.Empty;
618
619                         if (state != WriteState.Element)\r
620                                 throw new InvalidOperationException ("Invalid document state for attribute: " + state);\r
621 \r
622                         state = WriteState.Attribute;\r
623                         if (ns == XmlNamespaces.XMLNS)
624                                 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
625                         else
626                                 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
627                 }\r
628 \r
629                 private void ProcessNamespace (string prefix, string ns)
630                 {
631                         int nextTmp = hasLocalNs ? nsIndex : nodes [nodeIndex].FirstNamespace;
632
633                         nsIndex++;
634
635                         this.AddNsNode (nodeIndex,
636                                 prefix,
637                                 ns,
638                                 nextTmp);
639                         hasLocalNs = true;
640                         openNamespace = true;
641                 }
642
643                 private void ProcessAttribute (string prefix, string localName, string ns, string value)
644                 {
645                         if (prefix == null && ns.Length > 0)
646                                 prefix = LookupPrefix (ns);
647                         attributeIndex ++;
648
649                         this.AddAttribute (nodeIndex,
650                                 localName,
651                                 ns, 
652                                 prefix != null ? prefix : String.Empty, 
653                                 value,
654                                 0,
655                                 0);
656                         if (hasAttributes)
657                                 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
658                         else
659                                 hasAttributes = true;
660                 }
661
662                 public override void WriteEndAttribute ()\r
663                 {\r
664                         if (state != WriteState.Attribute)\r
665                                 throw new InvalidOperationException ();\r
666 \r
667                         openNamespace = false;\r
668                         state = WriteState.Element;\r
669                 }\r
670 \r
671                 public override void WriteString (string text)\r
672                 {\r
673                         if (WriteState == WriteState.Attribute) {
674                                 if (openNamespace) {\r
675                                         string value = atomicStringPool [namespaces [nsIndex].Namespace] + text;
676                                         namespaces [nsIndex].Namespace = AtomicIndex (value);
677                                 } else {\r
678                                         string value = nonAtomicStringPool [attributes [attributeIndex].Value] + text;
679                                         attributes [attributeIndex].Value = NonAtomicIndex (value);
680                                 }\r
681                         }\r
682                         else\r
683                                 AddTextNode (text);\r
684                 }\r
685 \r
686                 // Well, they cannot be supported, but actually used to\r
687                 // disable-output-escaping = "true"\r
688                 public override void WriteRaw (string data)\r
689                 {\r
690                         WriteString (data);\r
691                 }\r
692
693                 public override void WriteRaw (char [] data, int start, int len)\r
694                 {\r
695                         WriteString (new string (data, start, len));\r
696                 }\r
697 \r
698                 public override void WriteName (string name)\r
699                 {\r
700                         WriteString (name);\r
701                 }\r
702 \r
703                 public override void WriteNmToken (string name)\r
704                 {\r
705                         WriteString (name);\r
706                 }\r
707 \r
708                 public override void WriteBase64 (byte [] buffer, int index, int count)\r
709                 {\r
710                         throw new NotSupportedException ();\r
711                 }
712
713                 public override void WriteBinHex (byte [] buffer, int index, int count)\r
714                 {\r
715                         throw new NotSupportedException ();\r
716                 }\r
717 \r
718                 public override void WriteChars (char [] buffer, int index, int count)\r
719                 {\r
720                         throw new NotSupportedException ();\r
721                 }\r
722 \r
723                 public override void WriteCharEntity (char c)\r
724                 {\r
725                         throw new NotSupportedException ();\r
726                 }\r
727 \r
728                 public override void WriteDocType (string name, string pub, string sys, string intSubset)\r
729                 {\r
730                         throw new NotSupportedException ();\r
731                 }\r
732
733                 public override void WriteEntityRef (string name)
734                 {
735                         throw new NotSupportedException ();\r
736                 }
737
738                 public override void WriteQualifiedName (string localName, string ns)\r
739                 {\r
740                         throw new NotSupportedException ();\r
741                 }\r
742 \r
743                 public override void WriteSurrogateCharEntity (char high, char low)\r
744                 {\r
745                         throw new NotSupportedException ();\r
746                 }
747
748                 private bool IsWhitespace (string data)
749                 {
750                         for (int i = 0; i < data.Length; i++) {
751                                 switch (data [i]) {
752                                 case ' ':
753                                 case '\r':
754                                 case '\n':
755                                 case '\t':
756                                         continue;
757                                 default:
758                                         return false;
759                                 }
760                         }
761                         return true;
762                 }\r
763                 #endregion
764         }
765 }