New test.
[mono.git] / mcs / class / System.XML / System.Xml / XmlWriter.cs
1 //
2 // System.Xml.XmlWriter
3 //
4 // Authors:
5 //   Kral Ferch <kral_ferch@hotmail.com>
6 //   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
7 //
8 // (C) 2002 Kral Ferch
9 // (C) 2002-2003 Atsushi Enomoto
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.IO;
35 using System.Text;
36 #if NET_2_0
37 using System.Xml.XPath;
38 #endif
39
40 namespace System.Xml
41 {
42 #if NET_2_0
43         public abstract class XmlWriter : IDisposable
44 #else
45         public abstract class XmlWriter
46 #endif
47         {
48 #if NET_2_0
49                 XmlWriterSettings settings;
50 #endif
51
52                 #region Constructors
53
54                 protected XmlWriter () { }
55
56                 #endregion
57
58                 #region Properties
59
60 #if NET_2_0
61                 public virtual XmlWriterSettings Settings {
62                         get {
63                                 if (settings == null)
64                                         settings = new XmlWriterSettings ();
65                                 return settings;
66                         }
67                 }
68 #endif
69
70                 public abstract WriteState WriteState { get; }
71                 
72
73 #if NET_2_0
74                 public virtual string XmlLang {
75                         get { return null; }
76                 }
77
78                 public virtual XmlSpace XmlSpace {
79                         get { return XmlSpace.None; }
80                 }
81 #else
82                 public abstract string XmlLang { get; }
83
84                 public abstract XmlSpace XmlSpace { get; }
85 #endif
86
87                 #endregion
88
89                 #region Methods
90
91                 public abstract void Close ();
92
93 #if NET_2_0
94                 public static XmlWriter Create (Stream stream)
95                 {
96                         return Create (stream, null);
97                 }
98
99                 public static XmlWriter Create (string file)
100                 {
101                         return Create (file, null);
102                 }
103
104                 public static XmlWriter Create (TextWriter writer)
105                 {
106                         return Create (writer, null);
107                 }
108
109                 public static XmlWriter Create (XmlWriter writer)
110                 {
111                         return Create (writer, null);
112                 }
113
114                 public static XmlWriter Create (StringBuilder builder)
115                 {
116                         return Create (builder, null);
117                 }
118
119                 public static XmlWriter Create (Stream stream, XmlWriterSettings settings)
120                 {
121                         Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
122                         return Create (new StreamWriter (stream, enc), settings);
123                 }
124
125                 public static XmlWriter Create (string file, XmlWriterSettings settings)
126                 {
127                         Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
128                         return Create (new StreamWriter (file, false, enc), settings);
129                 }
130
131                 public static XmlWriter Create (StringBuilder builder, XmlWriterSettings settings)
132                 {
133                         return Create (new StringWriter (builder), settings);
134                 }
135
136                 public static XmlWriter Create (TextWriter writer, XmlWriterSettings settings)
137                 {
138                         return CreateTextWriter (writer, settings);
139                 }
140
141                 public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings)
142                 {
143                         if (settings == null)
144                                 settings = new XmlWriterSettings ();
145                         writer.settings = settings;
146                         return writer;
147                 }
148
149                 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings)
150                 {
151                         if (settings == null)
152                                 settings = new XmlWriterSettings ();
153                         XmlTextWriter xtw = new XmlTextWriter (writer, settings);
154                         return Create (xtw, settings);
155                 }
156
157                 protected virtual void Dispose (bool disposing)
158                 {
159                         Close ();
160                 }
161
162                 void IDisposable.Dispose ()
163                 {
164                         Dispose (false);
165                 }
166 #endif
167
168                 public abstract void Flush ();
169
170                 public abstract string LookupPrefix (string ns);
171
172                 private void WriteAttribute (XmlReader reader, bool defattr)
173                 {
174                         if (!defattr && reader.IsDefault)
175                                 return;
176
177                         WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
178                         while (reader.ReadAttributeValue ()) {
179                                 switch (reader.NodeType) {
180                                 case XmlNodeType.Text:
181                                         WriteString (reader.Value);
182                                         break;
183                                 case XmlNodeType.EntityReference:
184                                         WriteEntityRef (reader.Name);
185                                         break;
186                                 }
187                         }
188                         WriteEndAttribute ();
189                 }
190
191                 public virtual void WriteAttributes (XmlReader reader, bool defattr)
192                 {
193                         if(reader == null)
194                                 throw new ArgumentException("null XmlReader specified.", "reader");
195
196                         switch (reader.NodeType) {
197                         case XmlNodeType.XmlDeclaration:
198                                 WriteAttributeString ("version", reader ["version"]);
199                                 if (reader ["encoding"] != null)
200                                         WriteAttributeString ("encoding", reader ["encoding"]);
201                                 if (reader ["standalone"] != null)
202                                         WriteAttributeString ("standalone", reader ["standalone"]);
203                                 break;
204                         case XmlNodeType.Element:
205                                 if (reader.MoveToFirstAttribute ())
206                                         goto case XmlNodeType.Attribute;
207                                 break;
208                         case XmlNodeType.Attribute:
209                                 do {
210                                         WriteAttribute (reader, defattr);
211                                 } while (reader.MoveToNextAttribute ());
212                                 reader.MoveToElement ();
213                                 break;
214                         default:
215                                 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
216                         }
217                 }
218
219                 public void WriteAttributeString (string localName, string value)
220                 {
221                         WriteAttributeString ("", localName, null, value);
222                 }
223
224                 public void WriteAttributeString (string localName, string ns, string value)
225                 {
226                         WriteAttributeString ("", localName, ns, value);
227                 }
228
229                 public void WriteAttributeString (string prefix, string localName, string ns, string value)
230                 {
231                         // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
232                         // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
233
234                         WriteStartAttribute (prefix, localName, ns);
235                         if (value != null && value.Length > 0)
236                                 WriteString (value);
237                         WriteEndAttribute ();
238                 }
239
240                 public abstract void WriteBase64 (byte[] buffer, int index, int count);
241
242 #if NET_2_0
243                 public virtual void WriteBinHex (byte [] buffer, int index, int count)
244                 {
245                         StringWriter sw = new StringWriter ();
246                         XmlConvert.WriteBinHex (buffer, index, count, sw);
247                         WriteString (sw.ToString ());
248                 }
249 #else
250                 public abstract void WriteBinHex (byte[] buffer, int index, int count);
251 #endif
252
253                 public abstract void WriteCData (string text);
254
255                 public abstract void WriteCharEntity (char ch);
256
257                 public abstract void WriteChars (char[] buffer, int index, int count);
258
259                 public abstract void WriteComment (string text);
260
261                 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
262
263                 public void WriteElementString (string localName, string value)
264                 {
265                         WriteStartElement(localName);
266                         if (value != null && value.Length > 0)
267                                 WriteString(value);
268                         WriteEndElement();
269                 }
270
271                 public void WriteElementString (string localName, string ns, string value)
272                 {
273                         WriteStartElement(localName, ns);
274                         if (value != null && value.Length > 0)
275                                 WriteString(value);
276                         WriteEndElement();
277                 }
278
279 #if NET_2_0
280                 public void WriteElementString (string prefix, string localName, string ns, string value)
281                 {
282                         WriteStartElement(prefix, localName, ns);
283                         if (value != null && value.Length > 0)
284                                 WriteString(value);
285                         WriteEndElement();
286                 }
287 #endif
288
289                 public abstract void WriteEndAttribute ();
290
291                 public abstract void WriteEndDocument ();
292
293                 public abstract void WriteEndElement ();
294
295                 public abstract void WriteEntityRef (string name);
296
297                 public abstract void WriteFullEndElement ();
298
299 #if NET_2_0
300                 public virtual void WriteName (string name)
301                 {
302                         WriteNameInternal (name);
303                 }
304
305                 public virtual void WriteNmToken (string name)
306                 {
307                         WriteNmTokenInternal (name);
308                 }
309
310                 public virtual void WriteQualifiedName (string localName, string ns)
311                 {
312                         WriteQualifiedNameInternal (localName, ns);
313                 }
314 #else
315                 public abstract void WriteName (string name);
316
317                 public abstract void WriteNmToken (string name);
318
319                 public abstract void WriteQualifiedName (string localName, string ns);
320 #endif
321
322                 internal void WriteNameInternal (string name)
323                 {
324 #if NET_2_0
325                         switch (Settings.ConformanceLevel) {
326                         case ConformanceLevel.Document:
327                         case ConformanceLevel.Fragment:
328                                 XmlConvert.VerifyName (name);
329                                 break;
330                         }
331 #else
332                         XmlConvert.VerifyName (name);
333 #endif
334                         WriteString (name);
335                 }
336
337                 internal virtual void WriteNmTokenInternal (string name)
338                 {
339                         bool valid = true;
340 #if NET_2_0
341                         switch (Settings.ConformanceLevel) {
342                         case ConformanceLevel.Document:
343                         case ConformanceLevel.Fragment:
344                                 valid = XmlChar.IsNmToken (name);
345                                         break;
346                         }
347 #else
348                         valid = XmlChar.IsNmToken (name);
349 #endif
350                         if (!valid)
351                                 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
352                         WriteString (name);
353                 }
354
355                 internal void WriteQualifiedNameInternal (string localName, string ns)
356                 {
357                         if (localName == null || localName == String.Empty)
358                                 throw new ArgumentException ();
359                         if (ns == null)
360                                 ns = String.Empty;
361
362 #if NET_2_0
363                         switch (Settings.ConformanceLevel) {
364                         case ConformanceLevel.Document:
365                         case ConformanceLevel.Fragment:
366                                 XmlConvert.VerifyNCName (localName);
367                                 break;
368                         }
369 #else
370                         XmlConvert.VerifyNCName (localName);
371 #endif
372
373                         string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
374                         if (prefix == null)
375                                 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
376
377                         if (prefix != String.Empty) {
378                                 WriteString (prefix);
379                                 WriteString (":");
380                                 WriteString (localName);
381                         }
382                         else
383                                 WriteString (localName);
384                 }
385
386 #if NET_2_0
387                 [MonoTODO] // FIXME: test defattr handling
388                 public virtual void WriteNode (XPathNavigator navigator, bool defattr)
389                 {
390                         if (navigator == null)
391                                 throw new ArgumentNullException ("navigator");
392                         switch (navigator.NodeType) {
393                         case XPathNodeType.Attribute:
394                         case XPathNodeType.Namespace:
395                                 if (defattr || navigator.SchemaInfo == null ||
396                                     !navigator.SchemaInfo.IsDefault)
397                                         WriteAttributeString (navigator.Prefix, 
398                                                 navigator.LocalName,
399                                                 navigator.NamespaceURI,
400                                                 navigator.Value);
401                                 break;
402                         case XPathNodeType.Text:
403                                 WriteString (navigator.Value);
404                                 break;
405                         case XPathNodeType.SignificantWhitespace:
406                                 WriteWhitespace (navigator.Value);
407                                 break;
408                         case XPathNodeType.Whitespace:
409                                 WriteWhitespace (navigator.Value);
410                                 break;
411                         case XPathNodeType.Comment:
412                                 WriteComment (navigator.Value);
413                                 break;
414                         case XPathNodeType.ProcessingInstruction:
415                                 WriteProcessingInstruction (navigator.Name, navigator.Value);
416                                 break;
417                         case XPathNodeType.Root:
418                                 if (navigator.MoveToFirstChild ()) {
419                                         do {
420                                                 WriteNode (navigator, defattr);
421                                         } while (navigator.MoveToNext ());
422                                         navigator.MoveToParent ();
423                                 }
424                                 break;
425                         case XPathNodeType.Element:
426                                 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
427                                 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
428                                         do {
429                                                 WriteNode (navigator, defattr);
430                                         } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
431                                         navigator.MoveToParent ();
432                                 }
433                                 if (navigator.MoveToFirstAttribute ()) {
434                                         do {
435                                                 WriteNode (navigator, defattr);
436                                         } while (navigator.MoveToNextAttribute ());
437                                         navigator.MoveToParent ();
438                                 }
439                                 if (navigator.MoveToFirstChild ()) {
440                                         do {
441                                                 WriteNode (navigator, defattr);
442                                         } while (navigator.MoveToNext ());
443                                         navigator.MoveToParent ();
444                                 }
445                                 if (navigator.IsEmptyElement)
446                                         WriteEndElement ();
447                                 else
448                                         WriteFullEndElement ();
449                                 break;
450                         default:
451                                 throw new NotSupportedException ();
452                         }
453                 }
454 #endif
455
456                 public virtual void WriteNode (XmlReader reader, bool defattr)
457                 {
458                         if (reader == null)
459                                 throw new ArgumentException ();
460
461                         if (reader.ReadState == ReadState.Initial) {
462                                 reader.Read ();
463                                 do {
464                                         WriteNode (reader, defattr);
465                                 } while (!reader.EOF);
466                                 return;
467                         }
468
469                         switch (reader.NodeType) {
470                         case XmlNodeType.Element:
471                                 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
472 #if false
473                                 WriteAttributes (reader, defattr);
474                                 reader.MoveToElement ();
475 #else
476                                 // Well, I found that MS.NET took this way, since
477                                 // there was a error-prone SgmlReader that fails
478                                 // MoveToNextAttribute().
479                                 if (reader.HasAttributes) {
480                                         for (int i = 0; i < reader.AttributeCount; i++) {
481                                                 reader.MoveToAttribute (i);
482                                                 WriteAttribute (reader, defattr);
483                                         }
484                                         reader.MoveToElement ();
485                                 }
486 #endif
487                                 if (reader.IsEmptyElement)
488                                         WriteEndElement ();
489                                 else {
490                                         int depth = reader.Depth;
491                                         reader.Read ();
492                                         if (reader.NodeType != XmlNodeType.EndElement) {
493                                                 do {
494                                                         WriteNode (reader, defattr);
495                                                 } while (depth < reader.Depth);
496                                         }
497                                         WriteFullEndElement ();
498                                 }
499                                 break;
500                         // In case of XmlAttribute, don't proceed reader, and it will never be written.
501                         case XmlNodeType.Attribute:
502                                 return;
503                         case XmlNodeType.Text:
504                                 WriteString (reader.Value);
505                                 break;
506                         case XmlNodeType.CDATA:
507                                 WriteCData (reader.Value);
508                                 break;
509                         case XmlNodeType.EntityReference:
510                                 WriteEntityRef (reader.Name);
511                                 break;
512                         case XmlNodeType.XmlDeclaration:
513                                 // LAMESPEC: It means that XmlWriter implementation _must not_ check
514                                 // whether PI name is "xml" (it is XML error) or not.
515                         case XmlNodeType.ProcessingInstruction:
516                                 WriteProcessingInstruction (reader.Name, reader.Value);
517                                 break;
518                         case XmlNodeType.Comment:
519                                 WriteComment (reader.Value);
520                                 break;
521                         case XmlNodeType.DocumentType:
522                                 WriteDocType (reader.Name,
523                                         reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
524                                 break;
525                         case XmlNodeType.SignificantWhitespace:
526                                 goto case XmlNodeType.Whitespace;
527                         case XmlNodeType.Whitespace:
528                                 WriteWhitespace (reader.Value);
529                                 break;
530                         case XmlNodeType.EndElement:
531                                 WriteFullEndElement ();
532                                 break;
533                         case XmlNodeType.EndEntity:
534                                 break;
535                         case XmlNodeType.None:
536                                 break;  // Do nothing, nor reporting errors.
537                         default:
538                                 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
539                         }
540                         reader.Read ();
541                 }
542
543                 public abstract void WriteProcessingInstruction (string name, string text);
544
545                 public abstract void WriteRaw (string data);
546
547                 public abstract void WriteRaw (char[] buffer, int index, int count);
548
549 #if NET_2_0
550                 public void WriteStartAttribute (string localName)
551                 {
552                         WriteStartAttribute (null, localName, null);
553                 }
554 #endif
555
556                 public void WriteStartAttribute (string localName, string ns)
557                 {
558                         WriteStartAttribute (null, localName, ns);
559                 }
560
561                 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
562
563                 public abstract void WriteStartDocument ();
564
565                 public abstract void WriteStartDocument (bool standalone);
566
567                 public void WriteStartElement (string localName)
568                 {
569                         WriteStartElement (null, localName, null);
570                 }
571
572                 public void WriteStartElement (string localName, string ns)
573                 {
574                         WriteStartElement (null, localName, ns);
575                 }
576
577                 public abstract void WriteStartElement (string prefix, string localName, string ns);
578
579                 public abstract void WriteString (string text);
580
581                 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
582
583                 public abstract void WriteWhitespace (string ws);
584
585 #if NET_2_0
586                 [MonoTODO]
587                 public virtual void WriteValue (bool value)
588                 {
589                         WriteString (XQueryConvert.BooleanToString (value));
590                 }
591
592                 [MonoTODO]
593                 public virtual void WriteValue (DateTime value)
594                 {
595                         WriteString (XmlConvert.ToString (value));
596                 }
597
598                 [MonoTODO]
599                 public virtual void WriteValue (decimal value)
600                 {
601                         WriteString (XQueryConvert.DecimalToString (value));
602                 }
603
604                 [MonoTODO]
605                 public virtual void WriteValue (double value)
606                 {
607                         WriteString (XQueryConvert.DoubleToString (value));
608                 }
609
610                 [MonoTODO]
611                 public virtual void WriteValue (int value)
612                 {
613                         WriteString (XQueryConvert.IntToString (value));
614                 }
615
616                 [MonoTODO]
617                 public virtual void WriteValue (long value)
618                 {
619                         WriteString (XQueryConvert.IntegerToString (value));
620                 }
621
622                 [MonoTODO]
623                 public virtual void WriteValue (object value)
624                 {
625                         if (value is string)
626                                 WriteString ((string) value);
627                         else if (value is bool)
628                                 WriteValue ((bool) value);
629                         else if (value is DateTime)
630                                 WriteValue ((DateTime) value);
631                         else if (value is decimal)
632                                 WriteValue ((decimal) value);
633                         else if (value is double)
634                                 WriteValue ((double) value);
635                         else if (value is int)
636                                 WriteValue ((int) value);
637                         else if (value is long)
638                                 WriteValue ((long) value);
639                         else if (value is XmlQualifiedName) {
640                                 XmlQualifiedName qname = (XmlQualifiedName) value;
641                                 WriteQualifiedName (qname.Name, qname.Namespace);
642                         }
643                         else
644                                 throw new NotImplementedException ("Argument value is " + value);
645                 }
646
647                 [MonoTODO]
648                 public virtual void WriteValue (float value)
649                 {
650                         WriteString (XQueryConvert.FloatToString (value));
651                 }
652
653                 [MonoTODO]
654                 public virtual void WriteValue (string value)
655                 {
656                         WriteString (value);
657                 }
658 #endif
659
660                 #endregion
661         }
662 }