System.Drawing: added email to icon and test file headers
[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 { return settings; }
61                 }
62 #endif
63
64                 public abstract WriteState WriteState { get; }
65                 
66
67 #if NET_2_0
68                 public virtual string XmlLang {
69                         get { return null; }
70                 }
71
72                 public virtual XmlSpace XmlSpace {
73                         get { return XmlSpace.None; }
74                 }
75 #else
76                 public abstract string XmlLang { get; }
77
78                 public abstract XmlSpace XmlSpace { get; }
79 #endif
80
81                 #endregion
82
83                 #region Methods
84
85                 public abstract void Close ();
86
87 #if NET_2_0
88                 public static XmlWriter Create (Stream stream)
89                 {
90                         return Create (stream, null);
91                 }
92
93                 public static XmlWriter Create (string file)
94                 {
95                         return Create (file, null);
96                 }
97
98                 public static XmlWriter Create (TextWriter writer)
99                 {
100                         return Create (writer, null);
101                 }
102
103                 public static XmlWriter Create (XmlWriter writer)
104                 {
105                         return Create (writer, null);
106                 }
107
108                 public static XmlWriter Create (StringBuilder builder)
109                 {
110                         return Create (builder, null);
111                 }
112
113                 public static XmlWriter Create (Stream stream, XmlWriterSettings settings)
114                 {
115                         Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
116                         return Create (new StreamWriter (stream, enc), settings);
117                 }
118
119                 public static XmlWriter Create (string file, XmlWriterSettings settings)
120                 {
121                         Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
122                         return CreateTextWriter (new StreamWriter (file, false, enc), settings, true);
123                 }
124
125                 public static XmlWriter Create (StringBuilder builder, XmlWriterSettings settings)
126                 {
127                         return Create (new StringWriter (builder), settings);
128                 }
129
130                 public static XmlWriter Create (TextWriter writer, XmlWriterSettings settings)
131                 {
132                         if (settings == null)
133                                 settings = new XmlWriterSettings ();
134                         return CreateTextWriter (writer, settings, settings.CloseOutput);
135                 }
136
137                 public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings)
138                 {
139                         if (settings == null)
140                                 settings = new XmlWriterSettings ();
141                         else
142                                 settings = settings.Clone ();
143
144                         var src = writer.Settings;
145                         if (src == null) {
146                                 settings.ConformanceLevel = ConformanceLevel.Document; // Huh? Why??
147                                 writer = new DefaultXmlWriter (writer);
148                                 writer.settings = settings;
149                         } else {
150                                 ConformanceLevel dst = src.ConformanceLevel;
151                                 switch (src.ConformanceLevel) {
152                                 case ConformanceLevel.Auto:
153                                         dst = settings.ConformanceLevel;
154                                         break;
155                                 case ConformanceLevel.Document:
156                                 case ConformanceLevel.Fragment:
157                                         if (settings.ConformanceLevel != ConformanceLevel.Auto)
158                                                 dst = settings.ConformanceLevel;
159                                         break;
160                                 }
161
162                                 settings.MergeFrom (src);
163
164                                 // It returns a new XmlWriter instance if 1) Settings is null, or 2) Settings ConformanceLevel (or might be other members as well) give significant difference.
165                                 if (src.ConformanceLevel != dst) {
166                                         writer = new DefaultXmlWriter (writer, false);
167                                         writer.settings = settings;
168                                 }
169                         }
170
171                         return writer;
172                 }
173
174                 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput)
175                 {
176                         if (settings == null)
177                                 settings = new XmlWriterSettings ();
178                         XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput);
179                         return Create (xtw, settings);
180                 }
181
182                 protected virtual void Dispose (bool disposing)
183                 {
184                         Close ();
185                 }
186
187 #if NET_4_0 || MOBILE
188                 public void Dispose ()
189 #else
190                 void IDisposable.Dispose() 
191 #endif
192                 {
193                         Dispose (false);
194                 }
195 #endif
196
197                 public abstract void Flush ();
198
199                 public abstract string LookupPrefix (string ns);
200
201                 private void WriteAttribute (XmlReader reader, bool defattr)
202                 {
203                         if (!defattr && reader.IsDefault)
204                                 return;
205
206                         WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
207 #if MOONLIGHT
208                         // no ReadAttributeValue() in 2.1 profile.
209                         WriteString (reader.Value);
210 #else
211                         while (reader.ReadAttributeValue ()) {
212                                 switch (reader.NodeType) {
213                                 case XmlNodeType.Text:
214                                         WriteString (reader.Value);
215                                         break;
216                                 case XmlNodeType.EntityReference:
217                                         WriteEntityRef (reader.Name);
218                                         break;
219                                 }
220                         }
221 #endif
222                         WriteEndAttribute ();
223                 }
224
225                 public virtual void WriteAttributes (XmlReader reader, bool defattr)
226                 {
227                         if(reader == null)
228                                 throw new ArgumentException("null XmlReader specified.", "reader");
229
230                         switch (reader.NodeType) {
231                         case XmlNodeType.XmlDeclaration:
232                                 WriteAttributeString ("version", reader ["version"]);
233                                 if (reader ["encoding"] != null)
234                                         WriteAttributeString ("encoding", reader ["encoding"]);
235                                 if (reader ["standalone"] != null)
236                                         WriteAttributeString ("standalone", reader ["standalone"]);
237                                 break;
238                         case XmlNodeType.Element:
239                                 if (reader.MoveToFirstAttribute ())
240                                         goto case XmlNodeType.Attribute;
241                                 break;
242                         case XmlNodeType.Attribute:
243                                 do {
244                                         WriteAttribute (reader, defattr);
245                                 } while (reader.MoveToNextAttribute ());
246                                 reader.MoveToElement ();
247                                 break;
248                         default:
249                                 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
250                         }
251                 }
252
253                 public void WriteAttributeString (string localName, string value)
254                 {
255                         WriteAttributeString ("", localName, null, value);
256                 }
257
258                 public void WriteAttributeString (string localName, string ns, string value)
259                 {
260                         WriteAttributeString ("", localName, ns, value);
261                 }
262
263                 public void WriteAttributeString (string prefix, string localName, string ns, string value)
264                 {
265                         // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
266                         // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
267
268                         WriteStartAttribute (prefix, localName, ns);
269                         if (value != null && value.Length > 0)
270                                 WriteString (value);
271                         WriteEndAttribute ();
272                 }
273
274                 public abstract void WriteBase64 (byte[] buffer, int index, int count);
275
276 #if NET_2_0
277                 public virtual void WriteBinHex (byte [] buffer, int index, int count)
278                 {
279                         StringWriter sw = new StringWriter ();
280                         XmlConvert.WriteBinHex (buffer, index, count, sw);
281                         WriteString (sw.ToString ());
282                 }
283 #else
284                 public abstract void WriteBinHex (byte[] buffer, int index, int count);
285 #endif
286
287                 public abstract void WriteCData (string text);
288
289                 public abstract void WriteCharEntity (char ch);
290
291                 public abstract void WriteChars (char[] buffer, int index, int count);
292
293                 public abstract void WriteComment (string text);
294
295                 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
296
297                 public void WriteElementString (string localName, string value)
298                 {
299                         WriteStartElement(localName);
300                         if (value != null && value.Length > 0)
301                                 WriteString(value);
302                         WriteEndElement();
303                 }
304
305                 public void WriteElementString (string localName, string ns, string value)
306                 {
307                         WriteStartElement(localName, ns);
308                         if (value != null && value.Length > 0)
309                                 WriteString(value);
310                         WriteEndElement();
311                 }
312
313 #if NET_2_0
314                 public void WriteElementString (string prefix, string localName, string ns, string value)
315                 {
316                         WriteStartElement(prefix, localName, ns);
317                         if (value != null && value.Length > 0)
318                                 WriteString(value);
319                         WriteEndElement();
320                 }
321 #endif
322
323                 public abstract void WriteEndAttribute ();
324
325                 public abstract void WriteEndDocument ();
326
327                 public abstract void WriteEndElement ();
328
329                 public abstract void WriteEntityRef (string name);
330
331                 public abstract void WriteFullEndElement ();
332
333 #if NET_2_0
334                 public virtual void WriteName (string name)
335                 {
336                         WriteNameInternal (name);
337                 }
338
339                 public virtual void WriteNmToken (string name)
340                 {
341                         WriteNmTokenInternal (name);
342                 }
343
344                 public virtual void WriteQualifiedName (string localName, string ns)
345                 {
346                         WriteQualifiedNameInternal (localName, ns);
347                 }
348 #else
349                 public abstract void WriteName (string name);
350
351                 public abstract void WriteNmToken (string name);
352
353                 public abstract void WriteQualifiedName (string localName, string ns);
354 #endif
355
356                 internal void WriteNameInternal (string name)
357                 {
358 #if NET_2_0
359                         switch (Settings.ConformanceLevel) {
360                         case ConformanceLevel.Document:
361                         case ConformanceLevel.Fragment:
362                                 XmlConvert.VerifyName (name);
363                                 break;
364                         }
365 #else
366                         XmlConvert.VerifyName (name);
367 #endif
368                         WriteString (name);
369                 }
370
371                 internal virtual void WriteNmTokenInternal (string name)
372                 {
373                         bool valid = true;
374 #if NET_2_0
375                         switch (Settings.ConformanceLevel) {
376                         case ConformanceLevel.Document:
377                         case ConformanceLevel.Fragment:
378                                 valid = XmlChar.IsNmToken (name);
379                                         break;
380                         }
381 #else
382                         valid = XmlChar.IsNmToken (name);
383 #endif
384                         if (!valid)
385                                 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
386                         WriteString (name);
387                 }
388
389                 internal void WriteQualifiedNameInternal (string localName, string ns)
390                 {
391                         if (localName == null || localName == String.Empty)
392                                 throw new ArgumentException ();
393                         if (ns == null)
394                                 ns = String.Empty;
395
396 #if NET_2_0
397                         if (Settings != null) {
398                                 switch (Settings.ConformanceLevel) {
399                                 case ConformanceLevel.Document:
400                                 case ConformanceLevel.Fragment:
401                                         XmlConvert.VerifyNCName (localName);
402                                         break;
403                                 }
404                         }
405                         else
406                                 XmlConvert.VerifyNCName (localName);
407 #else
408                         XmlConvert.VerifyNCName (localName);
409 #endif
410
411                         string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
412                         if (prefix == null)
413                                 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
414
415                         if (prefix != String.Empty) {
416                                 WriteString (prefix);
417                                 WriteString (":");
418                                 WriteString (localName);
419                         }
420                         else
421                                 WriteString (localName);
422                 }
423
424 #if !MOONLIGHT
425                 public virtual void WriteNode (XPathNavigator navigator, bool defattr)
426                 {
427                         if (navigator == null)
428                                 throw new ArgumentNullException ("navigator");
429                         switch (navigator.NodeType) {
430                         case XPathNodeType.Attribute:
431                                 // no operation
432                                 break;
433                         case XPathNodeType.Namespace:
434                                 // no operation
435                                 break;
436                         case XPathNodeType.Text:
437                                 WriteString (navigator.Value);
438                                 break;
439                         case XPathNodeType.SignificantWhitespace:
440                                 WriteWhitespace (navigator.Value);
441                                 break;
442                         case XPathNodeType.Whitespace:
443                                 WriteWhitespace (navigator.Value);
444                                 break;
445                         case XPathNodeType.Comment:
446                                 WriteComment (navigator.Value);
447                                 break;
448                         case XPathNodeType.ProcessingInstruction:
449                                 WriteProcessingInstruction (navigator.Name, navigator.Value);
450                                 break;
451                         case XPathNodeType.Root:
452                                 if (navigator.MoveToFirstChild ()) {
453                                         do {
454                                                 WriteNode (navigator, defattr);
455                                         } while (navigator.MoveToNext ());
456                                         navigator.MoveToParent ();
457                                 }
458                                 break;
459                         case XPathNodeType.Element:
460                                 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
461                                 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
462                                         do {
463                                                 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
464                                                         WriteAttributeString (navigator.Prefix,
465                                                                 navigator.LocalName == String.Empty ? "xmlns" : navigator.LocalName,
466                                                                 "http://www.w3.org/2000/xmlns/",
467                                                                 navigator.Value);
468                                         } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
469                                         navigator.MoveToParent ();
470                                 }
471                                 if (navigator.MoveToFirstAttribute ()) {
472                                         do {
473                                                 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
474                                                         WriteAttributeString (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI, navigator.Value);
475
476                                         } while (navigator.MoveToNextAttribute ());
477                                         navigator.MoveToParent ();
478                                 }
479                                 if (navigator.MoveToFirstChild ()) {
480                                         do {
481                                                 WriteNode (navigator, defattr);
482                                         } while (navigator.MoveToNext ());
483                                         navigator.MoveToParent ();
484                                 }
485                                 if (navigator.IsEmptyElement)
486                                         WriteEndElement ();
487                                 else
488                                         WriteFullEndElement ();
489                                 break;
490                         default:
491                                 throw new NotSupportedException ();
492                         }
493                 }
494 #endif
495
496                 public virtual void WriteNode (XmlReader reader, bool defattr)
497                 {
498                         if (reader == null)
499                                 throw new ArgumentException ();
500
501                         if (reader.ReadState == ReadState.Initial) {
502                                 reader.Read ();
503                                 do {
504                                         WriteNode (reader, defattr);
505                                 } while (!reader.EOF);
506                                 return;
507                         }
508
509                         switch (reader.NodeType) {
510                         case XmlNodeType.Element:
511                                 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
512 #if false
513                                 WriteAttributes (reader, defattr);
514                                 reader.MoveToElement ();
515 #else
516                                 // Well, I found that MS.NET took this way, since
517                                 // there was a error-prone SgmlReader that fails
518                                 // MoveToNextAttribute().
519                                 if (reader.HasAttributes) {
520                                         for (int i = 0; i < reader.AttributeCount; i++) {
521                                                 reader.MoveToAttribute (i);
522                                                 WriteAttribute (reader, defattr);
523                                         }
524                                         reader.MoveToElement ();
525                                 }
526 #endif
527                                 if (reader.IsEmptyElement)
528                                         WriteEndElement ();
529                                 else {
530                                         int depth = reader.Depth;
531                                         reader.Read ();
532                                         if (reader.NodeType != XmlNodeType.EndElement) {
533                                                 do {
534                                                         WriteNode (reader, defattr);
535                                                 } while (depth < reader.Depth);
536                                         }
537                                         WriteFullEndElement ();
538                                 }
539                                 break;
540                         // In case of XmlAttribute, don't proceed reader, and it will never be written.
541                         case XmlNodeType.Attribute:
542                                 return;
543                         case XmlNodeType.Text:
544                                 WriteString (reader.Value);
545                                 break;
546                         case XmlNodeType.CDATA:
547                                 WriteCData (reader.Value);
548                                 break;
549                         case XmlNodeType.EntityReference:
550                                 WriteEntityRef (reader.Name);
551                                 break;
552                         case XmlNodeType.XmlDeclaration:
553                                 // LAMESPEC: It means that XmlWriter implementation _must not_ check
554                                 // whether PI name is "xml" (it is XML error) or not.
555                         case XmlNodeType.ProcessingInstruction:
556                                 WriteProcessingInstruction (reader.Name, reader.Value);
557                                 break;
558                         case XmlNodeType.Comment:
559                                 WriteComment (reader.Value);
560                                 break;
561                         case XmlNodeType.DocumentType:
562                                 WriteDocType (reader.Name,
563                                         reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
564                                 break;
565                         case XmlNodeType.SignificantWhitespace:
566                                 goto case XmlNodeType.Whitespace;
567                         case XmlNodeType.Whitespace:
568                                 WriteWhitespace (reader.Value);
569                                 break;
570                         case XmlNodeType.EndElement:
571                                 WriteFullEndElement ();
572                                 break;
573                         case XmlNodeType.EndEntity:
574                                 break;
575                         case XmlNodeType.None:
576                                 break;  // Do nothing, nor reporting errors.
577                         default:
578                                 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
579                         }
580                         reader.Read ();
581                 }
582
583                 public abstract void WriteProcessingInstruction (string name, string text);
584
585                 public abstract void WriteRaw (string data);
586
587                 public abstract void WriteRaw (char[] buffer, int index, int count);
588
589 #if NET_2_0
590                 public void WriteStartAttribute (string localName)
591                 {
592                         WriteStartAttribute (null, localName, null);
593                 }
594 #endif
595
596                 public void WriteStartAttribute (string localName, string ns)
597                 {
598                         WriteStartAttribute (null, localName, ns);
599                 }
600
601                 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
602
603                 public abstract void WriteStartDocument ();
604
605                 public abstract void WriteStartDocument (bool standalone);
606
607                 public void WriteStartElement (string localName)
608                 {
609                         WriteStartElement (null, localName, null);
610                 }
611
612                 public void WriteStartElement (string localName, string ns)
613                 {
614                         WriteStartElement (null, localName, ns);
615                 }
616
617                 public abstract void WriteStartElement (string prefix, string localName, string ns);
618
619                 public abstract void WriteString (string text);
620
621                 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
622
623                 public abstract void WriteWhitespace (string ws);
624
625 #if NET_2_0
626                 public virtual void WriteValue (bool value)
627                 {
628                         WriteString (XQueryConvert.BooleanToString (value));
629                 }
630
631                 public virtual void WriteValue (DateTime value)
632                 {
633                         WriteString (XmlConvert.ToString (value));
634                 }
635
636                 public virtual void WriteValue (decimal value)
637                 {
638                         WriteString (XQueryConvert.DecimalToString (value));
639                 }
640
641                 public virtual void WriteValue (double value)
642                 {
643                         WriteString (XQueryConvert.DoubleToString (value));
644                 }
645
646                 public virtual void WriteValue (int value)
647                 {
648                         WriteString (XQueryConvert.IntToString (value));
649                 }
650
651                 public virtual void WriteValue (long value)
652                 {
653                         WriteString (XQueryConvert.IntegerToString (value));
654                 }
655
656                 public virtual void WriteValue (object value)
657                 {
658                         if (value == null)
659                                 throw new ArgumentNullException ("value");
660
661                         if (value is string)
662                                 WriteString ((string) value);
663                         else if (value is bool)
664                                 WriteValue ((bool) value);
665                         else if (value is byte)
666                                 WriteValue ((int) value);
667                         else if (value is byte [])
668                                 WriteBase64 ((byte []) value, 0, ((byte []) value).Length);
669                         else if (value is char [])
670                                 WriteChars ((char []) value, 0, ((char []) value).Length);
671                         else if (value is DateTime)
672                                 WriteValue ((DateTime) value);
673                         else if (value is decimal)
674                                 WriteValue ((decimal) value);
675                         else if (value is double)
676                                 WriteValue ((double) value);
677                         else if (value is short)
678                                 WriteValue ((int) value);
679                         else if (value is int)
680                                 WriteValue ((int) value);
681                         else if (value is long)
682                                 WriteValue ((long) value);
683                         else if (value is float)
684                                 WriteValue ((float) value);
685                         else if (value is TimeSpan) // undocumented
686                                 WriteString (XmlConvert.ToString ((TimeSpan) value));
687                         else if (value is Uri)
688                                 WriteString (((Uri) value).ToString ());
689                         else if (value is XmlQualifiedName) {
690                                 XmlQualifiedName qname = (XmlQualifiedName) value;
691                                 if (!qname.Equals (XmlQualifiedName.Empty)) {
692                                         if (qname.Namespace.Length > 0 && LookupPrefix (qname.Namespace) == null)
693                                                 throw new InvalidCastException (String.Format ("The QName '{0}' cannot be written. No corresponding prefix is declared", qname));
694                                         WriteQualifiedName (qname.Name, qname.Namespace);
695                                 }
696                                 else
697                                         WriteString (String.Empty);
698                         }
699                         else if (value is IEnumerable) {
700                                 bool follow = false;
701                                 foreach (object obj in (IEnumerable) value) {
702                                         if (follow)
703                                                 WriteString (" ");
704                                         else
705                                                 follow = true;
706                                         WriteValue (obj);
707                                 }
708                         }
709                         else
710                                 throw new InvalidCastException (String.Format ("Type '{0}' cannot be cast to string", value.GetType ()));
711                 }
712
713                 public virtual void WriteValue (float value)
714                 {
715                         WriteString (XQueryConvert.FloatToString (value));
716                 }
717
718                 public virtual void WriteValue (string value)
719                 {
720                         WriteString (value);
721                 }
722 #endif
723
724                 #endregion
725         }
726 }