ensure the file is closed in XmlWriter.Create(filename)
[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 CreateTextWriter (new StreamWriter (file, false, enc), settings, true);
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                         if (settings == null)
139                                 settings = new XmlWriterSettings ();
140                         return CreateTextWriter (writer, settings, settings.CloseOutput);
141                 }
142
143                 public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings)
144                 {
145                         if (settings == null)
146                                 settings = new XmlWriterSettings ();
147                         writer.settings = settings;
148                         return writer;
149                 }
150
151                 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput)
152                 {
153                         if (settings == null)
154                                 settings = new XmlWriterSettings ();
155                         XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput);
156                         return Create (xtw, settings);
157                 }
158
159                 protected virtual void Dispose (bool disposing)
160                 {
161                         Close ();
162                 }
163
164                 void IDisposable.Dispose ()
165                 {
166                         Dispose (false);
167                 }
168 #endif
169
170                 public abstract void Flush ();
171
172                 public abstract string LookupPrefix (string ns);
173
174                 private void WriteAttribute (XmlReader reader, bool defattr)
175                 {
176                         if (!defattr && reader.IsDefault)
177                                 return;
178
179                         WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
180                         while (reader.ReadAttributeValue ()) {
181                                 switch (reader.NodeType) {
182                                 case XmlNodeType.Text:
183                                         WriteString (reader.Value);
184                                         break;
185                                 case XmlNodeType.EntityReference:
186                                         WriteEntityRef (reader.Name);
187                                         break;
188                                 }
189                         }
190                         WriteEndAttribute ();
191                 }
192
193                 public virtual void WriteAttributes (XmlReader reader, bool defattr)
194                 {
195                         if(reader == null)
196                                 throw new ArgumentException("null XmlReader specified.", "reader");
197
198                         switch (reader.NodeType) {
199                         case XmlNodeType.XmlDeclaration:
200                                 WriteAttributeString ("version", reader ["version"]);
201                                 if (reader ["encoding"] != null)
202                                         WriteAttributeString ("encoding", reader ["encoding"]);
203                                 if (reader ["standalone"] != null)
204                                         WriteAttributeString ("standalone", reader ["standalone"]);
205                                 break;
206                         case XmlNodeType.Element:
207                                 if (reader.MoveToFirstAttribute ())
208                                         goto case XmlNodeType.Attribute;
209                                 break;
210                         case XmlNodeType.Attribute:
211                                 do {
212                                         WriteAttribute (reader, defattr);
213                                 } while (reader.MoveToNextAttribute ());
214                                 reader.MoveToElement ();
215                                 break;
216                         default:
217                                 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
218                         }
219                 }
220
221                 public void WriteAttributeString (string localName, string value)
222                 {
223                         WriteAttributeString ("", localName, null, value);
224                 }
225
226                 public void WriteAttributeString (string localName, string ns, string value)
227                 {
228                         WriteAttributeString ("", localName, ns, value);
229                 }
230
231                 public void WriteAttributeString (string prefix, string localName, string ns, string value)
232                 {
233                         // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
234                         // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
235
236                         WriteStartAttribute (prefix, localName, ns);
237                         if (value != null && value.Length > 0)
238                                 WriteString (value);
239                         WriteEndAttribute ();
240                 }
241
242                 public abstract void WriteBase64 (byte[] buffer, int index, int count);
243
244 #if NET_2_0
245                 public virtual void WriteBinHex (byte [] buffer, int index, int count)
246                 {
247                         StringWriter sw = new StringWriter ();
248                         XmlConvert.WriteBinHex (buffer, index, count, sw);
249                         WriteString (sw.ToString ());
250                 }
251 #else
252                 public abstract void WriteBinHex (byte[] buffer, int index, int count);
253 #endif
254
255                 public abstract void WriteCData (string text);
256
257                 public abstract void WriteCharEntity (char ch);
258
259                 public abstract void WriteChars (char[] buffer, int index, int count);
260
261                 public abstract void WriteComment (string text);
262
263                 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
264
265                 public void WriteElementString (string localName, string value)
266                 {
267                         WriteStartElement(localName);
268                         if (value != null && value.Length > 0)
269                                 WriteString(value);
270                         WriteEndElement();
271                 }
272
273                 public void WriteElementString (string localName, string ns, string value)
274                 {
275                         WriteStartElement(localName, ns);
276                         if (value != null && value.Length > 0)
277                                 WriteString(value);
278                         WriteEndElement();
279                 }
280
281 #if NET_2_0
282                 public void WriteElementString (string prefix, string localName, string ns, string value)
283                 {
284                         WriteStartElement(prefix, localName, ns);
285                         if (value != null && value.Length > 0)
286                                 WriteString(value);
287                         WriteEndElement();
288                 }
289 #endif
290
291                 public abstract void WriteEndAttribute ();
292
293                 public abstract void WriteEndDocument ();
294
295                 public abstract void WriteEndElement ();
296
297                 public abstract void WriteEntityRef (string name);
298
299                 public abstract void WriteFullEndElement ();
300
301 #if NET_2_0
302                 public virtual void WriteName (string name)
303                 {
304                         WriteNameInternal (name);
305                 }
306
307                 public virtual void WriteNmToken (string name)
308                 {
309                         WriteNmTokenInternal (name);
310                 }
311
312                 public virtual void WriteQualifiedName (string localName, string ns)
313                 {
314                         WriteQualifiedNameInternal (localName, ns);
315                 }
316 #else
317                 public abstract void WriteName (string name);
318
319                 public abstract void WriteNmToken (string name);
320
321                 public abstract void WriteQualifiedName (string localName, string ns);
322 #endif
323
324                 internal void WriteNameInternal (string name)
325                 {
326 #if NET_2_0
327                         switch (Settings.ConformanceLevel) {
328                         case ConformanceLevel.Document:
329                         case ConformanceLevel.Fragment:
330                                 XmlConvert.VerifyName (name);
331                                 break;
332                         }
333 #else
334                         XmlConvert.VerifyName (name);
335 #endif
336                         WriteString (name);
337                 }
338
339                 internal virtual void WriteNmTokenInternal (string name)
340                 {
341                         bool valid = true;
342 #if NET_2_0
343                         switch (Settings.ConformanceLevel) {
344                         case ConformanceLevel.Document:
345                         case ConformanceLevel.Fragment:
346                                 valid = XmlChar.IsNmToken (name);
347                                         break;
348                         }
349 #else
350                         valid = XmlChar.IsNmToken (name);
351 #endif
352                         if (!valid)
353                                 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
354                         WriteString (name);
355                 }
356
357                 internal void WriteQualifiedNameInternal (string localName, string ns)
358                 {
359                         if (localName == null || localName == String.Empty)
360                                 throw new ArgumentException ();
361                         if (ns == null)
362                                 ns = String.Empty;
363
364 #if NET_2_0
365                         switch (Settings.ConformanceLevel) {
366                         case ConformanceLevel.Document:
367                         case ConformanceLevel.Fragment:
368                                 XmlConvert.VerifyNCName (localName);
369                                 break;
370                         }
371 #else
372                         XmlConvert.VerifyNCName (localName);
373 #endif
374
375                         string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
376                         if (prefix == null)
377                                 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
378
379                         if (prefix != String.Empty) {
380                                 WriteString (prefix);
381                                 WriteString (":");
382                                 WriteString (localName);
383                         }
384                         else
385                                 WriteString (localName);
386                 }
387
388 #if NET_2_0
389                 [MonoTODO] // FIXME: test defattr handling
390                 public virtual void WriteNode (XPathNavigator navigator, bool defattr)
391                 {
392                         if (navigator == null)
393                                 throw new ArgumentNullException ("navigator");
394                         switch (navigator.NodeType) {
395                         case XPathNodeType.Attribute:
396                         case XPathNodeType.Namespace:
397                                 if (defattr || navigator.SchemaInfo == null ||
398                                     !navigator.SchemaInfo.IsDefault)
399                                         WriteAttributeString (navigator.Prefix, 
400                                                 navigator.LocalName,
401                                                 navigator.NamespaceURI,
402                                                 navigator.Value);
403                                 break;
404                         case XPathNodeType.Text:
405                                 WriteString (navigator.Value);
406                                 break;
407                         case XPathNodeType.SignificantWhitespace:
408                                 WriteWhitespace (navigator.Value);
409                                 break;
410                         case XPathNodeType.Whitespace:
411                                 WriteWhitespace (navigator.Value);
412                                 break;
413                         case XPathNodeType.Comment:
414                                 WriteComment (navigator.Value);
415                                 break;
416                         case XPathNodeType.ProcessingInstruction:
417                                 WriteProcessingInstruction (navigator.Name, navigator.Value);
418                                 break;
419                         case XPathNodeType.Root:
420                                 if (navigator.MoveToFirstChild ()) {
421                                         do {
422                                                 WriteNode (navigator, defattr);
423                                         } while (navigator.MoveToNext ());
424                                         navigator.MoveToParent ();
425                                 }
426                                 break;
427                         case XPathNodeType.Element:
428                                 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
429                                 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
430                                         do {
431                                                 WriteNode (navigator, defattr);
432                                         } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
433                                         navigator.MoveToParent ();
434                                 }
435                                 if (navigator.MoveToFirstAttribute ()) {
436                                         do {
437                                                 WriteNode (navigator, defattr);
438                                         } while (navigator.MoveToNextAttribute ());
439                                         navigator.MoveToParent ();
440                                 }
441                                 if (navigator.MoveToFirstChild ()) {
442                                         do {
443                                                 WriteNode (navigator, defattr);
444                                         } while (navigator.MoveToNext ());
445                                         navigator.MoveToParent ();
446                                 }
447                                 if (navigator.IsEmptyElement)
448                                         WriteEndElement ();
449                                 else
450                                         WriteFullEndElement ();
451                                 break;
452                         default:
453                                 throw new NotSupportedException ();
454                         }
455                 }
456 #endif
457
458                 public virtual void WriteNode (XmlReader reader, bool defattr)
459                 {
460                         if (reader == null)
461                                 throw new ArgumentException ();
462
463                         if (reader.ReadState == ReadState.Initial) {
464                                 reader.Read ();
465                                 do {
466                                         WriteNode (reader, defattr);
467                                 } while (!reader.EOF);
468                                 return;
469                         }
470
471                         switch (reader.NodeType) {
472                         case XmlNodeType.Element:
473                                 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
474 #if false
475                                 WriteAttributes (reader, defattr);
476                                 reader.MoveToElement ();
477 #else
478                                 // Well, I found that MS.NET took this way, since
479                                 // there was a error-prone SgmlReader that fails
480                                 // MoveToNextAttribute().
481                                 if (reader.HasAttributes) {
482                                         for (int i = 0; i < reader.AttributeCount; i++) {
483                                                 reader.MoveToAttribute (i);
484                                                 WriteAttribute (reader, defattr);
485                                         }
486                                         reader.MoveToElement ();
487                                 }
488 #endif
489                                 if (reader.IsEmptyElement)
490                                         WriteEndElement ();
491                                 else {
492                                         int depth = reader.Depth;
493                                         reader.Read ();
494                                         if (reader.NodeType != XmlNodeType.EndElement) {
495                                                 do {
496                                                         WriteNode (reader, defattr);
497                                                 } while (depth < reader.Depth);
498                                         }
499                                         WriteFullEndElement ();
500                                 }
501                                 break;
502                         // In case of XmlAttribute, don't proceed reader, and it will never be written.
503                         case XmlNodeType.Attribute:
504                                 return;
505                         case XmlNodeType.Text:
506                                 WriteString (reader.Value);
507                                 break;
508                         case XmlNodeType.CDATA:
509                                 WriteCData (reader.Value);
510                                 break;
511                         case XmlNodeType.EntityReference:
512                                 WriteEntityRef (reader.Name);
513                                 break;
514                         case XmlNodeType.XmlDeclaration:
515                                 // LAMESPEC: It means that XmlWriter implementation _must not_ check
516                                 // whether PI name is "xml" (it is XML error) or not.
517                         case XmlNodeType.ProcessingInstruction:
518                                 WriteProcessingInstruction (reader.Name, reader.Value);
519                                 break;
520                         case XmlNodeType.Comment:
521                                 WriteComment (reader.Value);
522                                 break;
523                         case XmlNodeType.DocumentType:
524                                 WriteDocType (reader.Name,
525                                         reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
526                                 break;
527                         case XmlNodeType.SignificantWhitespace:
528                                 goto case XmlNodeType.Whitespace;
529                         case XmlNodeType.Whitespace:
530                                 WriteWhitespace (reader.Value);
531                                 break;
532                         case XmlNodeType.EndElement:
533                                 WriteFullEndElement ();
534                                 break;
535                         case XmlNodeType.EndEntity:
536                                 break;
537                         case XmlNodeType.None:
538                                 break;  // Do nothing, nor reporting errors.
539                         default:
540                                 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
541                         }
542                         reader.Read ();
543                 }
544
545                 public abstract void WriteProcessingInstruction (string name, string text);
546
547                 public abstract void WriteRaw (string data);
548
549                 public abstract void WriteRaw (char[] buffer, int index, int count);
550
551 #if NET_2_0
552                 public void WriteStartAttribute (string localName)
553                 {
554                         WriteStartAttribute (null, localName, null);
555                 }
556 #endif
557
558                 public void WriteStartAttribute (string localName, string ns)
559                 {
560                         WriteStartAttribute (null, localName, ns);
561                 }
562
563                 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
564
565                 public abstract void WriteStartDocument ();
566
567                 public abstract void WriteStartDocument (bool standalone);
568
569                 public void WriteStartElement (string localName)
570                 {
571                         WriteStartElement (null, localName, null);
572                 }
573
574                 public void WriteStartElement (string localName, string ns)
575                 {
576                         WriteStartElement (null, localName, ns);
577                 }
578
579                 public abstract void WriteStartElement (string prefix, string localName, string ns);
580
581                 public abstract void WriteString (string text);
582
583                 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
584
585                 public abstract void WriteWhitespace (string ws);
586
587 #if NET_2_0
588                 [MonoTODO]
589                 public virtual void WriteValue (bool value)
590                 {
591                         WriteString (XQueryConvert.BooleanToString (value));
592                 }
593
594                 [MonoTODO]
595                 public virtual void WriteValue (DateTime value)
596                 {
597                         WriteString (XmlConvert.ToString (value));
598                 }
599
600                 [MonoTODO]
601                 public virtual void WriteValue (decimal value)
602                 {
603                         WriteString (XQueryConvert.DecimalToString (value));
604                 }
605
606                 [MonoTODO]
607                 public virtual void WriteValue (double value)
608                 {
609                         WriteString (XQueryConvert.DoubleToString (value));
610                 }
611
612                 [MonoTODO]
613                 public virtual void WriteValue (int value)
614                 {
615                         WriteString (XQueryConvert.IntToString (value));
616                 }
617
618                 [MonoTODO]
619                 public virtual void WriteValue (long value)
620                 {
621                         WriteString (XQueryConvert.IntegerToString (value));
622                 }
623
624                 [MonoTODO]
625                 public virtual void WriteValue (object value)
626                 {
627                         if (value is string)
628                                 WriteString ((string) value);
629                         else if (value is bool)
630                                 WriteValue ((bool) value);
631                         else if (value is DateTime)
632                                 WriteValue ((DateTime) value);
633                         else if (value is decimal)
634                                 WriteValue ((decimal) value);
635                         else if (value is double)
636                                 WriteValue ((double) value);
637                         else if (value is int)
638                                 WriteValue ((int) value);
639                         else if (value is long)
640                                 WriteValue ((long) value);
641                         else if (value is XmlQualifiedName) {
642                                 XmlQualifiedName qname = (XmlQualifiedName) value;
643                                 WriteQualifiedName (qname.Name, qname.Namespace);
644                         }
645                         else
646                                 throw new NotImplementedException ("Argument value is " + value);
647                 }
648
649                 [MonoTODO]
650                 public virtual void WriteValue (float value)
651                 {
652                         WriteString (XQueryConvert.FloatToString (value));
653                 }
654
655                 [MonoTODO]
656                 public virtual void WriteValue (string value)
657                 {
658                         WriteString (value);
659                 }
660 #endif
661
662                 #endregion
663         }
664 }