**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / Mono.Xml / XmlNodeWriter.cs
blob49ada453ab67fce89e83a351093bc674be9e2573
1 //
2 // Mono.Xml.XmlNodeWriter
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.Xml;
33 namespace System.Xml
35 internal class XmlNodeWriter : XmlWriter
37 public XmlNodeWriter () : this (true)
41 // It should be public after some tests are done :-)
42 public XmlNodeWriter (bool isDocumentEntity)
44 doc = new XmlDocument ();
45 state = XmlNodeType.None;
46 this.isDocumentEntity = isDocumentEntity;
47 if (!isDocumentEntity)
48 current = fragment = doc.CreateDocumentFragment ();
51 XmlDocument doc;
52 bool isClosed;
53 // If it is not null, then we are now inside the element.
54 XmlNode current;
55 // If it is not null, then we are now inside the attribute.
56 XmlAttribute attribute;
57 // If it is false, then allow to contain multiple document elements.
58 bool isDocumentEntity;
59 XmlDocumentFragment fragment;
61 // None: started or closed.
62 // XmlDeclaration: after xmldecl. Never allow xmldecl.
63 // DocumentType: after doctype. Never allow xmldecl and doctype.
64 // Element: inside document element.
65 //
66 XmlNodeType state;
68 // Properties
69 public XmlNode Document {
70 get { return isDocumentEntity ? (XmlNode)doc : (XmlNode)fragment; }
73 public override WriteState WriteState {
74 get {
75 if (isClosed)
76 return WriteState.Closed;
77 if (attribute != null)
78 return WriteState.Attribute;
80 switch (state) {
81 case XmlNodeType.None:
82 return WriteState.Start;
83 case XmlNodeType.XmlDeclaration:
84 return WriteState.Prolog;
85 case XmlNodeType.DocumentType:
86 return WriteState.Element;
87 default:
88 return WriteState.Content;
93 public override string XmlLang {
94 get {
95 for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement)
96 if (n.HasAttribute ("xml:lang"))
97 return n.GetAttribute ("xml:lang");
98 return String.Empty;
102 public override XmlSpace XmlSpace {
103 get {
104 for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement) {
105 string xs = n.GetAttribute ("xml:space");
106 switch (xs) {
107 case "preserve":
108 return XmlSpace.Preserve;
109 case "default":
110 return XmlSpace.Default;
111 case "":
112 continue;
113 default:
114 throw new InvalidOperationException (String.Format ("Invalid xml:space {0}.", xs));
117 return XmlSpace.None;
121 // Private Methods
123 private void CheckState ()
125 if (isClosed)
126 throw new InvalidOperationException ();
130 private void WritePossiblyTopLevelNode (XmlNode n, bool possiblyAttribute)
132 CheckState ();
133 if (!possiblyAttribute && attribute != null)
134 throw new InvalidOperationException (String.Format ("Current state is not acceptable for {0}.", n.NodeType));
136 if (state != XmlNodeType.Element)
137 Document.AppendChild (n);
138 else if (attribute != null)
139 attribute.AppendChild (n);
140 else
141 current.AppendChild (n);
142 if (state == XmlNodeType.None)
143 state = XmlNodeType.XmlDeclaration;
146 // Public Methods
148 public override void Close ()
150 CheckState ();
151 isClosed = true;
154 public override void Flush ()
158 public override string LookupPrefix (string ns)
160 CheckState ();
161 if (current == null)
162 throw new InvalidOperationException ();
163 return current.GetPrefixOfNamespace (ns);
166 // StartDocument
168 public override void WriteStartDocument ()
170 WriteStartDocument (null);
173 public override void WriteStartDocument (bool standalone)
175 WriteStartDocument (standalone ? "yes" : "no");
178 private void WriteStartDocument (string sddecl)
180 CheckState ();
181 if (state != XmlNodeType.None)
182 throw new InvalidOperationException ("Current state is not acceptable for xmldecl.");
184 doc.AppendChild (doc.CreateXmlDeclaration ("1.0", null, sddecl));
185 state = XmlNodeType.XmlDeclaration;
188 // EndDocument
190 public override void WriteEndDocument ()
192 CheckState ();
194 isClosed = true;
197 // DocumentType
198 public override void WriteDocType (string name, string publicId, string systemId, string internalSubset)
200 CheckState ();
201 switch (state) {
202 case XmlNodeType.None:
203 case XmlNodeType.XmlDeclaration:
204 doc.AppendChild (doc.CreateDocumentType (name, publicId, systemId, internalSubset));
205 state = XmlNodeType.DocumentType;
206 break;
207 default:
208 throw new InvalidOperationException ("Current state is not acceptable for doctype.");
212 // StartElement
214 public override void WriteStartElement (string prefix, string name, string ns)
216 CheckState ();
217 if (isDocumentEntity && state == XmlNodeType.EndElement && doc.DocumentElement != null)
218 throw new InvalidOperationException ("Current state is not acceptable for startElement.");
220 XmlElement el = doc.CreateElement (prefix, name, ns);
221 if (current == null) {
222 Document.AppendChild (el);
223 state = XmlNodeType.Element;
224 } else {
225 current.AppendChild (el);
226 state = XmlNodeType.Element;
229 current = el;
232 // EndElement
234 public override void WriteEndElement ()
236 WriteEndElementInternal (false);
239 public override void WriteFullEndElement ()
241 WriteEndElementInternal (true);
244 private void WriteEndElementInternal (bool forceFull)
246 CheckState ();
247 if (current == null)
248 throw new InvalidOperationException ("Current state is not acceptable for endElement.");
250 if (!forceFull && current.FirstChild == null)
251 ((XmlElement) current).IsEmpty = true;
253 if (isDocumentEntity && current.ParentNode == doc)
254 state = XmlNodeType.EndElement;
255 else
256 current = current.ParentNode;
259 // StartAttribute
261 public override void WriteStartAttribute (string prefix, string name, string ns)
263 CheckState ();
264 if (attribute != null)
265 throw new InvalidOperationException ("There is an open attribute.");
266 if (!(current is XmlElement))
267 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
269 attribute = doc.CreateAttribute (prefix, name, ns);
270 ((XmlElement)current).SetAttributeNode (attribute);
273 public override void WriteEndAttribute ()
275 CheckState ();
276 if (attribute == null)
277 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
279 attribute = null;
282 public override void WriteCData (string data)
284 CheckState ();
285 if (current == null)
286 throw new InvalidOperationException ("Current state is not acceptable for CDATAsection.");
288 current.AppendChild (doc.CreateCDataSection (data));
291 public override void WriteComment (string comment)
293 WritePossiblyTopLevelNode (doc.CreateComment (comment), false);
296 public override void WriteProcessingInstruction (string name, string value)
298 WritePossiblyTopLevelNode (
299 doc.CreateProcessingInstruction (name, value), false);
302 public override void WriteEntityRef (string name)
304 WritePossiblyTopLevelNode (doc.CreateEntityReference (name), true);
307 public override void WriteCharEntity (char c)
309 WritePossiblyTopLevelNode (doc.CreateTextNode (new string (new char [] {c}, 0, 1)), true);
312 public override void WriteWhitespace (string ws)
314 WritePossiblyTopLevelNode (doc.CreateWhitespace (ws), true);
317 public override void WriteString (string data)
319 CheckState ();
320 if (current == null)
321 throw new InvalidOperationException ("Current state is not acceptable for Text.");
323 if (attribute != null)
324 attribute.AppendChild (doc.CreateTextNode (data));
325 else {
326 XmlText last = current.LastChild as XmlText;
327 if (last == null)
328 current.AppendChild(doc.CreateTextNode(data));
329 else
330 last.AppendData(data);
334 public override void WriteName (string name)
336 WriteString (name);
339 public override void WriteNmToken (string nmtoken)
341 WriteString (nmtoken);
344 public override void WriteQualifiedName (string name, string ns)
346 string prefix = LookupPrefix (ns);
347 if (prefix == null)
348 throw new ArgumentException (String.Format ("Invalid namespace {0}", ns));
349 if (prefix != String.Empty)
350 WriteString (name);
351 else
352 WriteString (prefix + ":" + name);
355 public override void WriteChars (char [] chars, int start, int len)
357 WriteString (new string (chars, start, len));
360 public override void WriteRaw (string data)
362 // It never supports raw string.
363 WriteString (data);
366 public override void WriteRaw (char [] chars, int start, int len)
368 // It never supports raw string.
369 WriteChars (chars, start, len);
372 public override void WriteBase64 (byte [] data, int start, int len)
374 // It never supports raw string.
375 WriteString (Convert.ToBase64String (data, start, len));
378 public override void WriteBinHex (byte [] data, int start, int len)
380 throw new NotImplementedException ();
383 public override void WriteSurrogateCharEntity (char c1, char c2)
385 throw new NotImplementedException ();