}
}
+ enum XmlDeclState {
+ Allow,
+ Ignore,
+ Auto,
+ Prohibit,
+ }
+
// Instance fields
Stream base_stream;
bool close_output_stream = true;
bool ignore_encoding;
bool namespaces = true;
- bool output_xmldecl = false;
+ XmlDeclState xmldecl_state = XmlDeclState.Allow;
bool check_character_validity;
NewLineHandling newline_handling = NewLineHandling.None;
XmlNodeInfo [] elements = new XmlNodeInfo [10];
Stack new_local_namespaces = new Stack ();
ArrayList explicit_nsdecls = new ArrayList ();
+ NamespaceHandling namespace_handling;
bool indent;
int indent_count = 2;
char quote_char = '"';
+ bool v2;
+
// Constructors
public XmlTextWriter (string filename, Encoding encoding)
public XmlTextWriter (TextWriter writer)
{
+ if (writer == null)
+ throw new ArgumentNullException ("writer");
+ ignore_encoding = (writer.Encoding == null);
Initialize (writer);
allow_doc_fragment = true;
}
#if NET_2_0
- XmlTextWriter (
- TextWriter writer, XmlWriterSettings settings)
+ internal XmlTextWriter (
+ TextWriter writer, XmlWriterSettings settings, bool closeOutput)
{
+ v2 = true;
+
if (settings == null)
settings = new XmlWriterSettings ();
Initialize (writer);
- close_output_stream = settings.CloseOutput;
+ close_output_stream = closeOutput;
allow_doc_fragment =
- settings.ConformanceLevel != System.Xml.ConformanceLevel.Document;
+ settings.ConformanceLevel != ConformanceLevel.Document;
+ switch (settings.ConformanceLevel) {
+ case ConformanceLevel.Auto:
+ xmldecl_state = settings.OmitXmlDeclaration ? XmlDeclState.Ignore : XmlDeclState.Allow;
+ break;
+ case ConformanceLevel.Document:
+ // LAMESPEC:
+ // On MSDN, XmlWriterSettings.OmitXmlDeclaration is documented as:
+ // "The XML declaration is always written if
+ // ConformanceLevel is set to Document, even
+ // if OmitXmlDeclaration is set to true. "
+ // but it is incorrect. It does consider
+ // OmitXmlDeclaration property.
+ xmldecl_state = settings.OmitXmlDeclaration ? XmlDeclState.Ignore : XmlDeclState.Auto;
+ break;
+ case ConformanceLevel.Fragment:
+ xmldecl_state = XmlDeclState.Prohibit;
+ break;
+ }
+ if (settings.Indent)
+ Formatting = Formatting.Indented;
indent_string = settings.IndentChars == null ?
String.Empty : settings.IndentChars;
if (settings.NewLineChars != null)
check_character_validity = settings.CheckCharacters;
newline_handling = settings.NewLineHandling;
- if (settings.OmitXmlDeclaration)
- output_xmldecl = false;
+ namespace_handling = settings.NamespaceHandling;
}
#endif
#if NET_2_0
// 2.0 XmlWriterSettings support
- internal bool CheckCharacters {
- set { check_character_validity = value; }
- }
-
- internal bool CloseOutput {
- set { close_output_stream = value; }
- }
-
// As for ConformanceLevel, MS.NET is inconsistent with
// MSDN documentation. For example, even if ConformanceLevel
// is set as .Auto, multiple WriteStartDocument() calls
// result in an error.
// ms-help://MS.NETFramework.v20.en/wd_xml/html/7db8802b-53d8-4735-a637-4d2d2158d643.htm
- [MonoTODO]
- internal ConformanceLevel ConformanceLevel {
- set {
- allow_doc_fragment = (value == System.Xml.ConformanceLevel.Fragment);
- }
- }
-
- internal string IndentChars {
- set { indent_string = (value == null) ? String.Empty : value; }
- }
-
- internal string NewLineChars {
- set { newline = (value == null) ? String.Empty : value; }
- }
- internal bool NewLineOnAttributes {
- set { indent_attributes = value; }
- }
-
- internal bool OmitXmlDeclaration {
- set { output_xmldecl = !value; }
- }
#endif
// Literal Output Control
public override void Close ()
{
- if (state == WriteState.Attribute)
- WriteEndAttribute ();
- while (open_count > 0)
- WriteEndElement ();
+#if NET_2_0
+ if (state != WriteState.Error) {
+#endif
+ if (state == WriteState.Attribute)
+ WriteEndAttribute ();
+ while (open_count > 0)
+ WriteEndElement ();
+#if NET_2_0
+ }
+#endif
if (close_output_stream)
writer.Close ();
if (state != WriteState.Start)
throw StateError ("XmlDeclaration");
+ switch (xmldecl_state) {
+ case XmlDeclState.Ignore:
+ return;
+ case XmlDeclState.Prohibit:
+ throw InvalidOperation ("WriteStartDocument cannot be called when ConformanceLevel is Fragment.");
+ }
+
+ state = WriteState.Prolog;
+
writer.Write ("<?xml version=");
writer.Write (quote_char);
writer.Write ("1.0");
}
writer.Write ("?>");
- output_xmldecl = false;
- state = WriteState.Prolog;
+ xmldecl_state = XmlDeclState.Ignore;
}
public override void WriteEndDocument ()
throw StateError ("DocType");
node_state = XmlNodeType.DocumentType;
- if (output_xmldecl)
+ if (xmldecl_state == XmlDeclState.Auto)
OutputAutoStartDocument ();
WriteIndent ();
if (!namespaces && prefix.Length > 0)
throw ArgumentError ("Namespace prefix is disabled in this XmlTextWriter.");
- if (prefix.Length > 0 && namespaceUri == null)
- throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+ // If namespace URI is empty, then either prefix
+ // must be empty as well, or there is an
+ // existing namespace mapping for the prefix.
+ if (prefix.Length > 0 && namespaceUri == null) {
+ namespaceUri = nsmanager.LookupNamespace (prefix, false);
+ if (namespaceUri == null || namespaceUri.Length == 0)
+ throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+ }
// Considering the fact that WriteStartAttribute()
// automatically changes argument namespaceURI, this
// is kind of silly implementation. See bug #77094.
throw new ArgumentException ("A prefix cannot be equivalent to \"xml\" in case-insensitive match.");
- if (output_xmldecl)
+ if (xmldecl_state == XmlDeclState.Auto)
OutputAutoStartDocument ();
if (state == WriteState.Element)
CloseStartElement ();
if (open_count == 0)
throw InvalidOperation ("There is no more open element.");
- bool isEmpty = state != WriteState.Content;
+ // bool isEmpty = state != WriteState.Content;
CloseStartElementCore ();
public override void WriteStartAttribute (
string prefix, string localName, string namespaceUri)
{
+ // LAMESPEC: this violates the expected behavior of
+ // this method, as it incorrectly allows unbalanced
+ // output of attributes. Microfot changes description
+ // on its behavior at their will, regardless of
+ // ECMA description.
+ if (state == WriteState.Attribute)
+ WriteEndAttribute ();
+
if (state != WriteState.Element && state != WriteState.Start)
throw StateError ("Attribute");
if (prefix == "xml")
namespaceUri = XmlNamespace;
// infer namespace URI.
- else if ((object) namespaceUri == null) {
+ else if ((object) namespaceUri == null || (v2 && namespaceUri.Length == 0)) {
if (isNSDecl)
namespaceUri = XmlnsNamespace;
else
if (isNSDecl && namespaceUri != XmlnsNamespace)
throw ArgumentError (String.Format ("The 'xmlns' attribute is bound to the reserved namespace '{0}'", XmlnsNamespace));
- // If namespace URI is empty, then prefix
- // must be empty as well.
- if (prefix.Length > 0 && namespaceUri.Length == 0)
- throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+ // If namespace URI is empty, then either prefix
+ // must be empty as well, or there is an
+ // existing namespace mapping for the prefix.
+ if (prefix.Length > 0 && namespaceUri.Length == 0) {
+ namespaceUri = nsmanager.LookupNamespace (prefix, false);
+ if (namespaceUri == null || namespaceUri.Length == 0)
+ throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+ }
// Dive into extremely complex procedure.
if (!isNSDecl && namespaceUri.Length > 0)
}
if (indent_attributes)
- WriteIndent ();
+ WriteIndentAttribute ();
else if (state != WriteState.Start)
writer.Write (' ');
value.Length == 0)
throw ArgumentError ("Non-empty prefix must be mapped to non-empty namespace URI.");
string existing = nsmanager.LookupNamespace (preserved_name, false);
- explicit_nsdecls.Add (preserved_name);
- if (open_count > 0 &&
- elements [open_count - 1].NS == String.Empty &&
- elements [open_count - 1].Prefix == preserved_name)
- ; // do nothing
- else if (existing != value)
- nsmanager.AddNamespace (preserved_name, value);
+
+ // consider OmitDuplicates here.
+ if ((namespace_handling & NamespaceHandling.OmitDuplicates) == 0 || existing != value)
+ explicit_nsdecls.Add (preserved_name);
+
+ if (open_count > 0) {
+
+ if (v2 &&
+ elements [open_count - 1].Prefix == preserved_name &&
+ elements [open_count - 1].NS != value)
+ throw new XmlException (String.Format ("Cannot redefine the namespace for prefix '{0}' used at current element", preserved_name));
+
+ if (elements [open_count - 1].NS == String.Empty &&
+ elements [open_count - 1].Prefix == preserved_name)
+ ; // do nothing
+ else if (existing != value)
+ nsmanager.AddNamespace (preserved_name, value);
+ }
} else {
switch (preserved_name) {
case "lang":
public override void WriteString (string text)
{
- if (text == null || text.Length == 0)
+ if (text == null || (text.Length == 0 && !v2))
return; // do nothing, including state transition.
ShiftStateContent ("Text", true);
ShiftStateContent ("QName", true);
- string prefix =
- state == WriteState.Content || ns.Length > 0 ?
- LookupPrefix (ns) : String.Empty;
+ string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
if (prefix == null) {
if (state == WriteState.Attribute)
prefix = MockupPrefix (ns, false);
void WriteIndent ()
{
- WriteIndentCore (0);
+ WriteIndentCore (0, false);
}
void WriteIndentEndElement ()
{
- WriteIndentCore (-1);
+ WriteIndentCore (-1, false);
}
- void WriteIndentCore (int nestFix)
+ void WriteIndentAttribute ()
+ {
+ if (!WriteIndentCore (0, true))
+ writer.Write (' '); // space is required instead.
+ }
+
+ bool WriteIndentCore (int nestFix, bool attribute)
{
if (!indent)
- return;
+ return false;
for (int i = open_count - 1; i >= 0; i--)
- if (elements [i].HasSimple)
- return;
+ if (!attribute && elements [i].HasSimple)
+ return false;
if (state != WriteState.Start)
writer.Write (newline);
for (int i = 0; i < open_count + nestFix; i++)
writer.Write (indent_string);
+ return true;
}
void OutputAutoStartDocument ()
case WriteState.Start:
if (isCharacter)
CheckMixedContentState ();
- if (output_xmldecl && !dontCheckXmlDecl)
+ if (xmldecl_state == XmlDeclState.Auto && !dontCheckXmlDecl)
OutputAutoStartDocument ();
state = WriteState.Prolog;
break;
case WriteState.Start:
if (!allow_doc_fragment || is_document_entity)
goto case WriteState.Closed;
- if (output_xmldecl)
+ if (xmldecl_state == XmlDeclState.Auto)
OutputAutoStartDocument ();
CheckMixedContentState ();
state = WriteState.Content;