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