Merge pull request #213 from linquize/linquize-master
[mono.git] / mcs / tools / monodoc / Monodoc / XmlNodeWriter.cs
1 //
2 // Mono.Xml.XmlNodeWriter
3 //
4 // Author:
5 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 //      (C)2003 Atsushi Enomoto
8 //
9 using System;
10 using System.Xml;
11
12 namespace Monodoc
13 {
14         public class CopyXmlNodeWriter : XmlWriter
15         {
16                 public CopyXmlNodeWriter () : this (true)
17                 {
18                 }
19
20                 // It should be public after some tests are done :-)
21                 public CopyXmlNodeWriter (bool isDocumentEntity)
22                 {
23                         doc = new XmlDocument ();
24                         state = XmlNodeType.None;
25                         this.isDocumentEntity = isDocumentEntity;
26                         if (!isDocumentEntity)
27                                 current = fragment = doc.CreateDocumentFragment ();
28                 }
29
30                 XmlDocument doc;
31                 bool isClosed;
32                 // If it is not null, then we are now inside the element.
33                 XmlNode current;
34                 // If it is not null, then we are now inside the attribute.
35                 XmlAttribute attribute;
36                 // If it is false, then allow to contain multiple document elements.
37                 bool isDocumentEntity;
38                 XmlDocumentFragment fragment;
39
40                 // None: started or closed.
41                 // XmlDeclaration: after xmldecl. Never allow xmldecl.
42                 // DocumentType: after doctype. Never allow xmldecl and doctype.
43                 // Element: inside document element.
44                 // 
45                 XmlNodeType state;
46
47                 // Properties
48                 public XmlNode Document {
49                         get { return isDocumentEntity ? (XmlNode)doc : (XmlNode)fragment; }
50                 }
51
52                 public override WriteState WriteState {
53                         get {
54                                 if (isClosed)
55                                         return WriteState.Closed;
56                                 if (attribute != null)
57                                         return WriteState.Attribute;
58
59                                 switch (state) {
60                                 case XmlNodeType.None:
61                                         return WriteState.Start;
62                                 case XmlNodeType.XmlDeclaration:
63                                         return WriteState.Prolog;
64                                 case XmlNodeType.DocumentType:
65                                         return WriteState.Element;
66                                 default:
67                                         return WriteState.Content;
68                                 }
69                         }
70                 }
71
72                 public override string XmlLang {
73                         get {
74                                 for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement)
75                                         if (n.HasAttribute ("xml:lang"))
76                                                 return n.GetAttribute ("xml:lang");
77                                 return String.Empty;
78                         }
79                 }
80
81                 public override XmlSpace XmlSpace {
82                         get {
83                                 for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement) {
84                                         string xs = n.GetAttribute ("xml:space");
85                                         switch (xs) {
86                                         case "preserve":
87                                                 return XmlSpace.Preserve;
88                                         case "default":
89                                                 return XmlSpace.Default;
90                                         case "":
91                                                 continue;
92                                         default:
93                                                 throw new InvalidOperationException (String.Format ("Invalid xml:space {0}.", xs));
94                                         }
95                                 }
96                                 return XmlSpace.None;
97                         }
98                 }
99
100                 // Private Methods
101
102                 private void CheckState ()
103                 {
104                         if (isClosed)
105                                 throw new InvalidOperationException ();
106
107                 }
108
109                 private void WritePossiblyTopLevelNode (XmlNode n, bool possiblyAttribute)
110                 {
111                         CheckState ();
112                         if (!possiblyAttribute && attribute != null)
113                                 throw new InvalidOperationException (String.Format ("Current state is not acceptable for {0}.", n.NodeType));
114
115                         if (state != XmlNodeType.Element)
116                                 Document.AppendChild (n);
117                         else if (attribute != null)
118                                 attribute.AppendChild (n);
119                         else
120                                 current.AppendChild (n);
121                         if (state == XmlNodeType.None)
122                                 state = XmlNodeType.XmlDeclaration;
123                 }
124
125                 // Public Methods
126
127                 public override void Close ()
128                 {
129                         CheckState ();
130                         isClosed = true;
131                 }
132
133                 public override void Flush ()
134                 {
135                 }
136
137                 public override string LookupPrefix (string ns)
138                 {
139                         CheckState ();
140                         if (current == null)
141                                 throw new InvalidOperationException ();
142                         return current.GetPrefixOfNamespace (ns);
143                 }
144
145                 // StartDocument
146
147                 public override void WriteStartDocument ()
148                 {
149                         WriteStartDocument (null);
150                 }
151
152                 public override void WriteStartDocument (bool standalone)
153                 {
154                         WriteStartDocument (standalone ? "yes" : "no");
155                 }
156
157                 private void WriteStartDocument (string sddecl)
158                 {
159                         CheckState ();
160                         if (state != XmlNodeType.None)
161                                 throw new InvalidOperationException ("Current state is not acceptable for xmldecl.");
162
163                         doc.AppendChild (doc.CreateXmlDeclaration ("1.0", null, sddecl));
164                         state = XmlNodeType.XmlDeclaration;
165                 }
166                 
167                 // EndDocument
168                 
169                 public override void WriteEndDocument ()
170                 {
171                         CheckState ();
172
173                         isClosed = true;
174                 }
175
176                 // DocumentType
177                 public override void WriteDocType (string name, string publicId, string systemId, string internalSubset)
178                 {
179                         CheckState ();
180                         switch (state) {
181                         case XmlNodeType.None:
182                         case XmlNodeType.XmlDeclaration:
183                                 doc.AppendChild (doc.CreateDocumentType (name, publicId, systemId, internalSubset));
184                                 state = XmlNodeType.DocumentType;
185                                 break;
186                         default:
187                                 throw new InvalidOperationException ("Current state is not acceptable for doctype.");
188                         }
189                 }
190
191                 // StartElement
192
193                 public override void WriteStartElement (string prefix, string name, string ns)
194                 {
195                         CheckState ();
196                         if (isDocumentEntity && state == XmlNodeType.EndElement && doc.DocumentElement != null)
197                                 throw new InvalidOperationException ("Current state is not acceptable for startElement.");
198
199                         XmlElement el = doc.CreateElement (prefix, name, ns);
200                         if (current == null) {
201                                 Document.AppendChild (el);
202                                 state = XmlNodeType.Element;
203                         } else {
204                                 current.AppendChild (el);
205                                 state = XmlNodeType.Element;
206                         }
207
208                         current = el;
209                 }
210
211                 // EndElement
212
213                 public override void WriteEndElement ()
214                 {
215                         WriteEndElementInternal (false);
216                 }
217                 
218                 public override void WriteFullEndElement ()
219                 {
220                         WriteEndElementInternal (true);
221                 }
222
223                 private void WriteEndElementInternal (bool forceFull)
224                 {
225                         CheckState ();
226                         if (current == null)
227                                 throw new InvalidOperationException ("Current state is not acceptable for endElement.");
228
229                         if (!forceFull && current.FirstChild == null)
230                                 ((XmlElement) current).IsEmpty = true;
231
232                         if (isDocumentEntity && current.ParentNode == doc)
233                                 state = XmlNodeType.EndElement;
234                         else
235                                 current = current.ParentNode;
236                 }
237
238                 // StartAttribute
239
240                 public override void WriteStartAttribute (string prefix, string name, string ns)
241                 {
242                         CheckState ();
243                         if (attribute != null)
244                                 throw new InvalidOperationException ("There is an open attribute.");
245                         if (!(current is XmlElement))
246                                 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
247
248                         attribute = doc.CreateAttribute (prefix, name, ns);
249                         ((XmlElement)current).SetAttributeNode (attribute);
250                 }
251
252                 public override void WriteEndAttribute ()
253                 {
254                         CheckState ();
255                         if (attribute == null)
256                                 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
257
258                         attribute = null;
259                 }
260
261                 public override void WriteCData (string data)
262                 {
263                         CheckState ();
264                         if (current == null)
265                                 throw new InvalidOperationException ("Current state is not acceptable for CDATAsection.");
266
267                         current.AppendChild (doc.CreateCDataSection (data));
268                 }
269
270                 public override void WriteComment (string comment)
271                 {
272                         WritePossiblyTopLevelNode (doc.CreateComment (comment), false);
273                 }
274
275                 public override void WriteProcessingInstruction (string name, string value)
276                 {
277                         WritePossiblyTopLevelNode (
278                                 doc.CreateProcessingInstruction (name, value), false);
279                 }
280
281                 public override void WriteEntityRef (string name)
282                 {
283                         WritePossiblyTopLevelNode (doc.CreateEntityReference (name), true);
284                 }
285
286                 public override void WriteCharEntity (char c)
287                 {
288                         WritePossiblyTopLevelNode (doc.CreateTextNode (new string (new char [] {c}, 0, 1)), true);
289                 }
290
291                 public override void WriteWhitespace (string ws)
292                 {
293                         WritePossiblyTopLevelNode (doc.CreateWhitespace (ws), true);
294                 }
295
296                 public override void WriteString (string data)
297                 {
298                         CheckState ();
299                         if (current == null)
300                                 throw new InvalidOperationException ("Current state is not acceptable for Text.");
301
302                         if (attribute != null)
303                                 attribute.AppendChild (doc.CreateTextNode (data));
304                         else {
305                                 XmlText last = current.LastChild as XmlText;
306                                 if (last == null)
307                                         current.AppendChild(doc.CreateTextNode(data));
308                                 else 
309                                         last.AppendData(data);
310                         }
311                 }
312
313                 public override void WriteName (string name)
314                 {
315                         WriteString (name);
316                 }
317
318                 public override void WriteNmToken (string nmtoken)
319                 {
320                         WriteString (nmtoken);
321                 }
322
323                 public override void WriteQualifiedName (string name, string ns)
324                 {
325                         string prefix = LookupPrefix (ns);
326                         if (prefix == null)
327                                 throw new ArgumentException (String.Format ("Invalid namespace {0}", ns));
328                         if (prefix != String.Empty)
329                                 WriteString (name);
330                         else
331                                 WriteString (prefix + ":" + name);
332                 }
333
334                 public override void WriteChars (char [] chars, int start, int len)
335                 {
336                         WriteString (new string (chars, start, len));
337                 }
338
339                 public override void WriteRaw (string data)
340                 {
341                         // It never supports raw string.
342                         WriteString (data);
343                 }
344
345                 public override void WriteRaw (char [] chars, int start, int len)
346                 {
347                         // It never supports raw string.
348                         WriteChars (chars, start, len);
349                 }
350
351                 public override void WriteBase64 (byte [] data, int start, int len)
352                 {
353                         // It never supports raw string.
354                         WriteString (Convert.ToBase64String (data, start, len));
355                 }
356                 
357                 public override void WriteBinHex (byte [] data, int start, int len)
358                 {
359                         throw new NotImplementedException ();
360                 }
361
362                 public override void WriteSurrogateCharEntity (char c1, char c2)
363                 {
364                         throw new NotImplementedException ();
365                 }
366         }
367 }