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