a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
389 lines
9.9 KiB
C#
389 lines
9.9 KiB
C#
//
|
|
// Mono.Xml.XmlNodeWriter
|
|
//
|
|
// Author:
|
|
// Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
|
|
//
|
|
// (C)2003 Atsushi Enomoto
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
using System;
|
|
using System.Xml;
|
|
|
|
namespace System.Xml
|
|
{
|
|
internal class XmlNodeWriter : XmlWriter
|
|
{
|
|
public XmlNodeWriter () : this (true)
|
|
{
|
|
}
|
|
|
|
// It should be public after some tests are done :-)
|
|
public XmlNodeWriter (bool isDocumentEntity)
|
|
{
|
|
doc = new XmlDocument ();
|
|
state = XmlNodeType.None;
|
|
this.isDocumentEntity = isDocumentEntity;
|
|
if (!isDocumentEntity)
|
|
current = fragment = doc.CreateDocumentFragment ();
|
|
}
|
|
|
|
XmlDocument doc;
|
|
bool isClosed;
|
|
// If it is not null, then we are now inside the element.
|
|
XmlNode current;
|
|
// If it is not null, then we are now inside the attribute.
|
|
XmlAttribute attribute;
|
|
// If it is false, then allow to contain multiple document elements.
|
|
bool isDocumentEntity;
|
|
XmlDocumentFragment fragment;
|
|
|
|
// None: started or closed.
|
|
// XmlDeclaration: after xmldecl. Never allow xmldecl.
|
|
// DocumentType: after doctype. Never allow xmldecl and doctype.
|
|
// Element: inside document element.
|
|
//
|
|
XmlNodeType state;
|
|
|
|
// Properties
|
|
public XmlNode Document {
|
|
get { return isDocumentEntity ? (XmlNode)doc : (XmlNode)fragment; }
|
|
}
|
|
|
|
public override WriteState WriteState {
|
|
get {
|
|
if (isClosed)
|
|
return WriteState.Closed;
|
|
if (attribute != null)
|
|
return WriteState.Attribute;
|
|
|
|
switch (state) {
|
|
case XmlNodeType.None:
|
|
return WriteState.Start;
|
|
case XmlNodeType.XmlDeclaration:
|
|
return WriteState.Prolog;
|
|
case XmlNodeType.DocumentType:
|
|
return WriteState.Element;
|
|
default:
|
|
return WriteState.Content;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string XmlLang {
|
|
get {
|
|
for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement)
|
|
if (n.HasAttribute ("xml:lang"))
|
|
return n.GetAttribute ("xml:lang");
|
|
return String.Empty;
|
|
}
|
|
}
|
|
|
|
public override XmlSpace XmlSpace {
|
|
get {
|
|
for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement) {
|
|
string xs = n.GetAttribute ("xml:space");
|
|
switch (xs) {
|
|
case "preserve":
|
|
return XmlSpace.Preserve;
|
|
case "default":
|
|
return XmlSpace.Default;
|
|
case "":
|
|
continue;
|
|
default:
|
|
throw new InvalidOperationException (String.Format ("Invalid xml:space {0}.", xs));
|
|
}
|
|
}
|
|
return XmlSpace.None;
|
|
}
|
|
}
|
|
|
|
// Private Methods
|
|
|
|
private void CheckState ()
|
|
{
|
|
if (isClosed)
|
|
throw new InvalidOperationException ();
|
|
|
|
}
|
|
|
|
private void WritePossiblyTopLevelNode (XmlNode n, bool possiblyAttribute)
|
|
{
|
|
CheckState ();
|
|
if (!possiblyAttribute && attribute != null)
|
|
throw new InvalidOperationException (String.Format ("Current state is not acceptable for {0}.", n.NodeType));
|
|
|
|
if (state != XmlNodeType.Element)
|
|
Document.AppendChild (n);
|
|
else if (attribute != null)
|
|
attribute.AppendChild (n);
|
|
else
|
|
current.AppendChild (n);
|
|
if (state == XmlNodeType.None)
|
|
state = XmlNodeType.XmlDeclaration;
|
|
}
|
|
|
|
// Public Methods
|
|
|
|
public override void Close ()
|
|
{
|
|
CheckState ();
|
|
isClosed = true;
|
|
}
|
|
|
|
public override void Flush ()
|
|
{
|
|
}
|
|
|
|
public override string LookupPrefix (string ns)
|
|
{
|
|
CheckState ();
|
|
if (current == null)
|
|
throw new InvalidOperationException ();
|
|
return current.GetPrefixOfNamespace (ns);
|
|
}
|
|
|
|
// StartDocument
|
|
|
|
public override void WriteStartDocument ()
|
|
{
|
|
WriteStartDocument (null);
|
|
}
|
|
|
|
public override void WriteStartDocument (bool standalone)
|
|
{
|
|
WriteStartDocument (standalone ? "yes" : "no");
|
|
}
|
|
|
|
private void WriteStartDocument (string sddecl)
|
|
{
|
|
CheckState ();
|
|
if (state != XmlNodeType.None)
|
|
throw new InvalidOperationException ("Current state is not acceptable for xmldecl.");
|
|
|
|
doc.AppendChild (doc.CreateXmlDeclaration ("1.0", null, sddecl));
|
|
state = XmlNodeType.XmlDeclaration;
|
|
}
|
|
|
|
// EndDocument
|
|
|
|
public override void WriteEndDocument ()
|
|
{
|
|
CheckState ();
|
|
|
|
isClosed = true;
|
|
}
|
|
|
|
// DocumentType
|
|
public override void WriteDocType (string name, string publicId, string systemId, string internalSubset)
|
|
{
|
|
CheckState ();
|
|
switch (state) {
|
|
case XmlNodeType.None:
|
|
case XmlNodeType.XmlDeclaration:
|
|
doc.AppendChild (doc.CreateDocumentType (name, publicId, systemId, internalSubset));
|
|
state = XmlNodeType.DocumentType;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException ("Current state is not acceptable for doctype.");
|
|
}
|
|
}
|
|
|
|
// StartElement
|
|
|
|
public override void WriteStartElement (string prefix, string name, string ns)
|
|
{
|
|
CheckState ();
|
|
if (isDocumentEntity && state == XmlNodeType.EndElement && doc.DocumentElement != null)
|
|
throw new InvalidOperationException ("Current state is not acceptable for startElement.");
|
|
|
|
XmlElement el = doc.CreateElement (prefix, name, ns);
|
|
if (current == null) {
|
|
Document.AppendChild (el);
|
|
state = XmlNodeType.Element;
|
|
} else {
|
|
current.AppendChild (el);
|
|
state = XmlNodeType.Element;
|
|
}
|
|
|
|
current = el;
|
|
}
|
|
|
|
// EndElement
|
|
|
|
public override void WriteEndElement ()
|
|
{
|
|
WriteEndElementInternal (false);
|
|
}
|
|
|
|
public override void WriteFullEndElement ()
|
|
{
|
|
WriteEndElementInternal (true);
|
|
}
|
|
|
|
private void WriteEndElementInternal (bool forceFull)
|
|
{
|
|
CheckState ();
|
|
if (current == null)
|
|
throw new InvalidOperationException ("Current state is not acceptable for endElement.");
|
|
|
|
if (!forceFull && current.FirstChild == null)
|
|
((XmlElement) current).IsEmpty = true;
|
|
|
|
if (isDocumentEntity && current.ParentNode == doc)
|
|
state = XmlNodeType.EndElement;
|
|
else
|
|
current = current.ParentNode;
|
|
}
|
|
|
|
// StartAttribute
|
|
|
|
public override void WriteStartAttribute (string prefix, string name, string ns)
|
|
{
|
|
CheckState ();
|
|
if (attribute != null)
|
|
throw new InvalidOperationException ("There is an open attribute.");
|
|
if (!(current is XmlElement))
|
|
throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
|
|
|
|
attribute = doc.CreateAttribute (prefix, name, ns);
|
|
((XmlElement)current).SetAttributeNode (attribute);
|
|
}
|
|
|
|
public override void WriteEndAttribute ()
|
|
{
|
|
CheckState ();
|
|
if (attribute == null)
|
|
throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
|
|
|
|
attribute = null;
|
|
}
|
|
|
|
public override void WriteCData (string data)
|
|
{
|
|
CheckState ();
|
|
if (current == null)
|
|
throw new InvalidOperationException ("Current state is not acceptable for CDATAsection.");
|
|
|
|
current.AppendChild (doc.CreateCDataSection (data));
|
|
}
|
|
|
|
public override void WriteComment (string comment)
|
|
{
|
|
WritePossiblyTopLevelNode (doc.CreateComment (comment), false);
|
|
}
|
|
|
|
public override void WriteProcessingInstruction (string name, string value)
|
|
{
|
|
WritePossiblyTopLevelNode (
|
|
doc.CreateProcessingInstruction (name, value), false);
|
|
}
|
|
|
|
public override void WriteEntityRef (string name)
|
|
{
|
|
WritePossiblyTopLevelNode (doc.CreateEntityReference (name), true);
|
|
}
|
|
|
|
public override void WriteCharEntity (char c)
|
|
{
|
|
WritePossiblyTopLevelNode (doc.CreateTextNode (new string (new char [] {c}, 0, 1)), true);
|
|
}
|
|
|
|
public override void WriteWhitespace (string ws)
|
|
{
|
|
WritePossiblyTopLevelNode (doc.CreateWhitespace (ws), true);
|
|
}
|
|
|
|
public override void WriteString (string data)
|
|
{
|
|
CheckState ();
|
|
if (current == null)
|
|
throw new InvalidOperationException ("Current state is not acceptable for Text.");
|
|
|
|
if (attribute != null)
|
|
attribute.AppendChild (doc.CreateTextNode (data));
|
|
else {
|
|
XmlText last = current.LastChild as XmlText;
|
|
if (last == null)
|
|
current.AppendChild(doc.CreateTextNode(data));
|
|
else
|
|
last.AppendData(data);
|
|
}
|
|
}
|
|
|
|
public override void WriteName (string name)
|
|
{
|
|
WriteString (name);
|
|
}
|
|
|
|
public override void WriteNmToken (string nmtoken)
|
|
{
|
|
WriteString (nmtoken);
|
|
}
|
|
|
|
public override void WriteQualifiedName (string name, string ns)
|
|
{
|
|
string prefix = LookupPrefix (ns);
|
|
if (prefix == null)
|
|
throw new ArgumentException (String.Format ("Invalid namespace {0}", ns));
|
|
if (prefix != String.Empty)
|
|
WriteString (name);
|
|
else
|
|
WriteString (prefix + ":" + name);
|
|
}
|
|
|
|
public override void WriteChars (char [] chars, int start, int len)
|
|
{
|
|
WriteString (new string (chars, start, len));
|
|
}
|
|
|
|
public override void WriteRaw (string data)
|
|
{
|
|
// It never supports raw string.
|
|
WriteString (data);
|
|
}
|
|
|
|
public override void WriteRaw (char [] chars, int start, int len)
|
|
{
|
|
// It never supports raw string.
|
|
WriteChars (chars, start, len);
|
|
}
|
|
|
|
public override void WriteBase64 (byte [] data, int start, int len)
|
|
{
|
|
// It never supports raw string.
|
|
WriteString (Convert.ToBase64String (data, start, len));
|
|
}
|
|
|
|
public override void WriteBinHex (byte [] data, int start, int len)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
public override void WriteSurrogateCharEntity (char c1, char c2)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
}
|