XmlTextWriter work on BaseStream, WriteState, and Namespaces.
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextWriter.cs
1 //
2 // System.Xml.XmlTextWriter
3 //
4 // Author:
5 //   Kral Ferch <kral_ferch@hotmail.com>
6 //
7 // (C) 2002 Kral Ferch
8 //
9
10 using System;
11 using System.Collections;
12 using System.IO;
13 using System.Text;
14
15 namespace System.Xml
16 {
17         public class XmlTextWriter : XmlWriter
18         {
19                 #region Fields
20
21                 protected TextWriter w;
22                 protected bool nullEncoding = false;
23                 protected bool openWriter = true;
24                 protected bool openStartElement;
25                 protected bool documentStarted = false;
26                 protected bool namespaces = true;
27                 protected Stack openElements = new Stack ();
28                 protected XmlNamespaceManager namespaceManager = new XmlNamespaceManager (new NameTable ());
29                 protected Formatting formatting = Formatting.None;
30                 protected int indentation = 2;
31                 protected char indentChar = ' ';
32                 protected string indentChars = "  ";
33                 protected char quoteChar = '\"';
34                 protected int indentLevel = 0;
35                 protected string indentFormatting;
36                 protected Stream baseStream = null;
37
38                 #endregion
39
40                 #region Constructors
41
42                 public XmlTextWriter (TextWriter w) : base ()
43                 {
44                         this.w = w;
45                         
46                         try {
47                                 baseStream = ((StreamWriter)w).BaseStream;
48                         }
49                         catch (Exception) { }
50                 }
51
52                 public XmlTextWriter (Stream w, Encoding encoding) : base ()
53                 {
54                         if (encoding == null) {
55                                 nullEncoding = true;
56                                 encoding = new UTF8Encoding ();
57                         }
58
59                         this.w = new StreamWriter(w, encoding);
60                         baseStream = w;
61                 }
62
63                 public XmlTextWriter (string filename, Encoding encoding) : base ()
64                 {
65                         this.w = new StreamWriter(filename, false, encoding);
66                         baseStream = ((StreamWriter)w).BaseStream;
67                 }
68
69                 #endregion
70
71                 #region Properties
72
73                 public Stream BaseStream {
74                         get { return baseStream; }
75                 }
76
77
78                 public Formatting Formatting {
79                         get { return formatting; }
80                         set { formatting = value; }
81                 }
82
83                 public bool IndentingOverriden 
84                 {
85                         get {
86                                 if (openElements.Count == 0)
87                                         return false;
88                                 else
89                                         return (((XmlTextWriterOpenElement)openElements.Peek()).IndentingOverriden);
90                         }
91                         set {
92                                 if (openElements.Count > 0)
93                                         ((XmlTextWriterOpenElement)openElements.Peek()).IndentingOverriden = value;
94                         }
95                 }
96
97                 public int Indentation {
98                         get { return indentation; }
99                         set {
100                                 indentation = value;
101                                 UpdateIndentChars ();
102                         }
103                 }
104
105                 public char IndentChar {
106                         get { return indentChar; }
107                         set {
108                                 indentChar = value;
109                                 UpdateIndentChars ();
110                         }
111                 }
112
113                 public bool Namespaces {
114                         get { return namespaces; }
115                         set {
116                                 if (ws != WriteState.Start)
117                                         throw new InvalidOperationException ("NotInWriteState.");
118                                 
119                                 namespaces = value;
120                         }
121                 }
122
123                 [MonoTODO]
124                 public char QuoteChar {
125                         get { return quoteChar; }
126                         set {
127                                 if ((value != '\'') && (value != '\"'))
128                                         throw new ArgumentException ("This is an invalid XML attribute quote character. Valid attribute quote characters are ' and \".");
129                                 
130                                 quoteChar = value;
131                         }
132                 }
133
134                 public override WriteState WriteState {
135                         get { return ws; }
136                 }
137                 
138                 [MonoTODO]
139                 public override string XmlLang {
140                         get { throw new NotImplementedException(); }
141                 }
142
143                 [MonoTODO]
144                 public override XmlSpace XmlSpace {
145                         get { throw new NotImplementedException(); }
146                 }
147
148                 #endregion
149
150                 #region Methods
151
152                 private void CheckState ()
153                 {
154                         if (!openWriter) {
155                                 throw new InvalidOperationException ("The Writer is closed.");
156                         }
157
158                         if ((documentStarted == true) && (formatting == Formatting.Indented) && (!IndentingOverriden)) {
159                                 indentFormatting = "\r\n";
160                                 if (indentLevel > 0) {
161                                         for (int i = 0; i < indentLevel; i++)
162                                                 indentFormatting += indentChars;
163                                 }
164                         }
165                         else
166                                 indentFormatting = "";
167
168                         documentStarted = true;
169                 }
170
171                 public override void Close ()
172                 {
173                         while (openElements.Count > 0) {
174                                 WriteEndElement();
175                         }
176
177                         w.Close();
178                         ws = WriteState.Closed;
179                         openWriter = false;
180                 }
181
182                 private void CloseStartElement ()
183                 {
184                         if (openStartElement) 
185                         {
186                                 w.Write(">");
187                                 ws = WriteState.Content;
188                                 openStartElement = false;
189                         }
190                 }
191
192                 public override void Flush ()
193                 {
194                         w.Flush ();
195                 }
196
197                 [MonoTODO]
198                 public override string LookupPrefix (string ns)
199                 {
200                         throw new NotImplementedException ();
201                 }
202
203                 private void UpdateIndentChars ()
204                 {
205                         indentChars = "";
206                         for (int i = 0; i < indentation; i++)
207                                 indentChars += indentChar;
208                 }
209
210                 [MonoTODO]
211                 public override void WriteBase64 (byte[] buffer, int index, int count)
212                 {
213                         throw new NotImplementedException ();
214                 }
215
216                 [MonoTODO]
217                 public override void WriteBinHex (byte[] buffer, int index, int count)
218                 {
219                         throw new NotImplementedException ();
220                 }
221
222                 public override void WriteCData (string text)
223                 {
224                         if (text.IndexOf("]]>") > 0) 
225                         {
226                                 throw new ArgumentException ();
227                         }
228
229                         CheckState ();
230                         CloseStartElement ();
231
232                         w.Write("<![CDATA[{0}]]>", text);
233                 }
234
235                 [MonoTODO]
236                 public override void WriteCharEntity (char ch)
237                 {
238                         throw new NotImplementedException ();
239                 }
240
241                 [MonoTODO]
242                 public override void WriteChars (char[] buffer, int index, int count)
243                 {
244                         throw new NotImplementedException ();
245                 }
246
247                 public override void WriteComment (string text)
248                 {
249                         if ((text.EndsWith("-")) || (text.IndexOf("-->") > 0)) {
250                                 throw new ArgumentException ();
251                         }
252
253                         CheckState ();
254                         CloseStartElement ();
255
256                         w.Write ("<!--{0}-->", text);
257                 }
258
259                 [MonoTODO]
260                 public override void WriteDocType (string name, string pubid, string sysid, string subset)
261                 {
262                         throw new NotImplementedException ();
263                 }
264
265                 [MonoTODO]
266                 public override void WriteEndAttribute ()
267                 {
268                         throw new NotImplementedException ();
269                 }
270
271                 [MonoTODO]
272                 public override void WriteEndDocument ()
273                 {
274                         throw new NotImplementedException ();
275                 }
276
277                 public override void WriteEndElement ()
278                 {
279                         if (openElements.Count == 0)
280                                 throw new InvalidOperationException("There was no XML start tag open.");
281
282                         indentLevel--;
283
284                         CheckState ();
285
286                         if (openStartElement) {
287                                 w.Write (" />");
288                                 openElements.Pop ();
289                                 openStartElement = false;
290                         }
291                         else {
292                                 w.Write ("{0}</{1}>", indentFormatting, openElements.Pop ());
293                                 namespaceManager.PopScope();
294                         }
295                 }
296
297                 [MonoTODO]
298                 public override void WriteEntityRef (string name)
299                 {
300                         throw new NotImplementedException ();
301                 }
302
303                 [MonoTODO]
304                 public override void WriteFullEndElement ()
305                 {
306                         throw new NotImplementedException ();
307                 }
308
309                 [MonoTODO]
310                 public override void WriteName (string name)
311                 {
312                         throw new NotImplementedException ();
313                 }
314
315                 [MonoTODO]
316                 public override void WriteNmToken (string name)
317                 {
318                         throw new NotImplementedException ();
319                 }
320
321                 public override void WriteProcessingInstruction (string name, string text)
322                 {
323                         if ((name == null) || (name == string.Empty) || (name.IndexOf("?>") > 0) || (text.IndexOf("?>") > 0)) {
324                                 throw new ArgumentException ();
325                         }
326
327                         CheckState ();
328                         CloseStartElement ();
329
330                         w.Write ("{0}<?{1} {2}?>", indentFormatting, name, text);
331                 }
332
333                 [MonoTODO]
334                 public override void WriteQualifiedName (string localName, string ns)
335                 {
336                         throw new NotImplementedException ();
337                 }
338
339                 [MonoTODO]
340                 public override void WriteRaw (string data)
341                 {
342                         throw new NotImplementedException ();
343                 }
344
345                 [MonoTODO]
346                 public override void WriteRaw (char[] buffer, int index, int count)
347                 {
348                         throw new NotImplementedException ();
349                 }
350
351                 [MonoTODO]
352                 public override void WriteStartAttribute (string prefix, string localName, string ns)
353                 {
354                         throw new NotImplementedException ();
355                 }
356
357                 public override void WriteStartDocument ()
358                 {
359                         WriteStartDocument ("");
360                 }
361
362                 public override void WriteStartDocument (bool standalone)
363                 {
364                         string standaloneFormatting;
365
366                         if (standalone == true)
367                                 standaloneFormatting = " standalone=\"yes\"";
368                         else
369                                 standaloneFormatting = " standalone=\"no\"";
370
371                         WriteStartDocument (standaloneFormatting);
372                 }
373
374                 private void WriteStartDocument (string standaloneFormatting)
375                 {
376                         if (documentStarted == true)
377                                 throw new InvalidOperationException("WriteStartDocument should be the first call.");
378
379                         CheckState ();
380
381                         string encodingFormatting = "";
382
383                         if (!nullEncoding)
384                                 encodingFormatting = " encoding=\"" + w.Encoding.HeaderName + "\"";
385
386                         w.Write("<?xml version=\"1.0\"{0}{1}?>", encodingFormatting, standaloneFormatting);
387                         ws = WriteState.Prolog;
388                 }
389
390                 public override void WriteStartElement (string prefix, string localName, string ns)
391                 {
392                         if (!Namespaces && (((prefix != null) && (prefix != String.Empty))
393                                 || ((ns != null) && (ns != String.Empty))))
394                                 throw new ArgumentException ("Cannot set the namespace if Namespaces is 'false'.");
395
396                         WriteStartElementInternal (prefix, localName, ns);
397                 }
398
399                 protected override void WriteStartElementInternal (string prefix, string localName, string ns)
400                 {
401                         if (prefix == null)
402                                 prefix = String.Empty;
403
404                         if (ns == null)
405                                 ns = String.Empty;
406
407                         if ((prefix != String.Empty) && ((ns == null) || (ns == String.Empty)))
408                                 throw new ArgumentException ("Cannot use a prefix with an empty namespace.");
409
410                         CheckState ();
411                         CloseStartElement ();
412
413                         string formatXmlns = "";
414                         string formatPrefix = "";
415
416                         if (ns != String.Empty) 
417                         {
418                                 string existingPrefix = namespaceManager.LookupPrefix (ns);
419
420                                 if (prefix == String.Empty)
421                                         prefix = existingPrefix;
422
423                                 if (prefix != existingPrefix)
424                                         formatXmlns = " xmlns:" + prefix + "=\"" + ns + "\"";
425                                 else if (existingPrefix == String.Empty)
426                                         formatXmlns = " xmlns=\"" + ns + "\"";
427                         }
428                         else if ((prefix == String.Empty) && (namespaceManager.LookupNamespace(prefix) != String.Empty)) {
429                                 formatXmlns = " xmlns=\"\"";
430                         }
431
432                         if (prefix != String.Empty) {
433                                 formatPrefix = prefix + ":";
434                         }
435
436                         w.Write ("{0}<{1}{2}{3}", indentFormatting, formatPrefix, localName, formatXmlns);
437
438                         openElements.Push (new XmlTextWriterOpenElement (formatPrefix + localName));
439                         ws = WriteState.Element;
440                         openStartElement = true;
441
442                         namespaceManager.PushScope ();
443                         namespaceManager.AddNamespace (prefix, ns);
444
445                         indentLevel++;
446                 }
447
448                 [MonoTODO("Haven't done any entity replacements yet.")]
449                 public override void WriteString (string text)
450                 {
451                         if (text != String.Empty) {
452                                 CheckState ();
453                                 CloseStartElement ();
454                                 w.Write (text);
455                         }
456
457                         IndentingOverriden = true;
458                 }
459
460                 [MonoTODO]
461                 public override void WriteSurrogateCharEntity (char lowChar, char highChar)
462                 {
463                         throw new NotImplementedException ();
464                 }
465
466                 [MonoTODO]
467                 public override void WriteWhitespace (string ws)
468                 {
469                         throw new NotImplementedException ();
470                 }
471
472                 #endregion
473         }
474 }