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