2 // Mono.Xml.XmlNodeWriter
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C)2003 Atsushi Enomoto
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
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 ();
53 // If it is not null, then we are now inside the element.
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.
69 public XmlNode Document
{
70 get { return isDocumentEntity ? (XmlNode)doc : (XmlNode)fragment; }
73 public override WriteState WriteState
{
76 return WriteState
.Closed
;
77 if (attribute
!= null)
78 return WriteState
.Attribute
;
81 case XmlNodeType
.None
:
82 return WriteState
.Start
;
83 case XmlNodeType
.XmlDeclaration
:
84 return WriteState
.Prolog
;
85 case XmlNodeType
.DocumentType
:
86 return WriteState
.Element
;
88 return WriteState
.Content
;
93 public override string XmlLang
{
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");
102 public override XmlSpace XmlSpace
{
104 for (XmlElement n
= current
as XmlElement
; n
!= null; n
= n
.ParentNode
as XmlElement
) {
105 string xs
= n
.GetAttribute ("xml:space");
108 return XmlSpace
.Preserve
;
110 return XmlSpace
.Default
;
114 throw new InvalidOperationException (String
.Format ("Invalid xml:space {0}.", xs
));
117 return XmlSpace
.None
;
123 private void CheckState ()
126 throw new InvalidOperationException ();
130 private void WritePossiblyTopLevelNode (XmlNode n
, bool possiblyAttribute
)
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
);
141 current
.AppendChild (n
);
142 if (state
== XmlNodeType
.None
)
143 state
= XmlNodeType
.XmlDeclaration
;
148 public override void Close ()
154 public override void Flush ()
158 public override string LookupPrefix (string ns
)
162 throw new InvalidOperationException ();
163 return current
.GetPrefixOfNamespace (ns
);
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
)
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
;
190 public override void WriteEndDocument ()
198 public override void WriteDocType (string name
, string publicId
, string systemId
, string internalSubset
)
202 case XmlNodeType
.None
:
203 case XmlNodeType
.XmlDeclaration
:
204 doc
.AppendChild (doc
.CreateDocumentType (name
, publicId
, systemId
, internalSubset
));
205 state
= XmlNodeType
.DocumentType
;
208 throw new InvalidOperationException ("Current state is not acceptable for doctype.");
214 public override void WriteStartElement (string prefix
, string name
, string ns
)
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
;
225 current
.AppendChild (el
);
226 state
= XmlNodeType
.Element
;
234 public override void WriteEndElement ()
236 WriteEndElementInternal (false);
239 public override void WriteFullEndElement ()
241 WriteEndElementInternal (true);
244 private void WriteEndElementInternal (bool forceFull
)
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
;
256 current
= current
.ParentNode
;
261 public override void WriteStartAttribute (string prefix
, string name
, string ns
)
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 ()
276 if (attribute
== null)
277 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
282 public override void WriteCData (string data
)
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
)
321 throw new InvalidOperationException ("Current state is not acceptable for Text.");
323 if (attribute
!= null)
324 attribute
.AppendChild (doc
.CreateTextNode (data
));
326 XmlText last
= current
.LastChild
as XmlText
;
328 current
.AppendChild(doc
.CreateTextNode(data
));
330 last
.AppendData(data
);
334 public override void WriteName (string 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
);
348 throw new ArgumentException (String
.Format ("Invalid namespace {0}", ns
));
349 if (prefix
!= String
.Empty
)
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.
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 ();