Explorar el Código

2003-07-06 Atsushi Enomoto <[email protected]>

	* XmlElement.cs : Removed extraneous xmlns output support (it should
	  be done by XmlTextWriter).
	* XmlNode.cs : InsertBefore() now checks validity of the newChild (and
	  most of the changes are in fact indentation changes).
	* XmlWriter.cs : WriteAttributes() now can write entity references.
	  WriteNode() now writes xmldecl attributes correctly.
	  Removed namespaceManager and xmlns check logic, since it should be
	  done by individual XmlTextWriter.
	* XmlTextWriter.cs : Patch by Jerome. AddMissingElementXmlns () has
	  written multiple xmlns declarations.
	  Moved namespaceManager from abstract XmlWriter.
	  Write{Start|End}Attribute() and WriteString() now checks xmlns
	  attribute value. (MS.NET holds value only with this write method).

svn path=/trunk/mcs/; revision=15982
Atsushi Eno hace 22 años
padre
commit
3f7e68b275

+ 16 - 0
mcs/class/System.XML/System.Xml/ChangeLog

@@ -1,3 +1,19 @@
+2003-07-06  Atsushi Enomoto <[email protected]>
+
+	* XmlElement.cs : Removed extraneous xmlns output support (it should
+	  be done by XmlTextWriter).
+	* XmlNode.cs : InsertBefore() now checks validity of the newChild (and
+	  most of the changes are in fact indentation changes).
+	* XmlWriter.cs : WriteAttributes() now can write entity references.
+	  WriteNode() now writes xmldecl attributes correctly.
+	  Removed namespaceManager and xmlns check logic, since it should be 
+	  done by individual XmlTextWriter.
+	* XmlTextWriter.cs : Patch by Jerome. AddMissingElementXmlns () has
+	  written multiple xmlns declarations.
+	  Moved namespaceManager from abstract XmlWriter.
+	  Write{Start|End}Attribute() and WriteString() now checks xmlns 
+	  attribute value. (MS.NET holds value only with this write method).
+
 2003-07-04  Atsushi Enomoto <[email protected]>
 
 	* XmlDocument.cs : removed xmlReader.Close() from Load(XmlReader).

+ 3 - 2
mcs/class/System.XML/System.Xml/XmlElement.cs

@@ -379,7 +379,8 @@ namespace System.Xml
 
 			foreach(XmlNode attributeNode in Attributes)
 				attributeNode.WriteTo(w);
-
+/*
+// It should be automatically written by XmlWriter.
 			// write namespace declarations(if not exist)
 			foreach(XmlNode attributeNode in Attributes) {
 				if(attributeNode.Prefix != null && attributeNode.Prefix != String.Empty &&
@@ -387,7 +388,7 @@ namespace System.Xml
 					attributeNode.Prefix != "xmlns")
 					w.WriteAttributeString("xmlns", attributeNode.Prefix, "http://www.w3.org/2000/xmlns/", attributeNode.NamespaceURI);
 			}
-
+*/
 			if (IsEmpty)
 				w.WriteEndElement ();
 			else {

+ 41 - 17
mcs/class/System.XML/System.Xml/XmlNode.cs

@@ -308,30 +308,54 @@ namespace System.Xml
 		{
 			XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
 
-			if (NodeType == XmlNodeType.Document ||
-			    NodeType == XmlNodeType.Element ||
-			    NodeType == XmlNodeType.Attribute ||
-			    NodeType == XmlNodeType.DocumentFragment) {
-				if (IsReadOnly)
-					throw new ArgumentException ("The specified node is readonly.");
+			if (NodeType != XmlNodeType.Element &&
+			    NodeType != XmlNodeType.Attribute &&
+			    NodeType != XmlNodeType.Document &&
+			    NodeType != XmlNodeType.DocumentFragment)
+				throw new InvalidOperationException (String.Format ("current node {0} is not allowed to have any children.", NodeType));
 
-				if (newChild.OwnerDocument != ownerDoc)
-					throw new ArgumentException ("Can't append a node created by another document.");
+			switch (NodeType) {
+			case XmlNodeType.Attribute:
+				switch (newChild.NodeType) {
+				case XmlNodeType.Text:
+				case XmlNodeType.EntityReference:
+					break;
+				default:
+					throw new ArgumentException (String.Format (
+						"Cannot insert specified type of node {0} as a child of this node {0}.", 
+						newChild.NodeType, NodeType));
+				}
+				break;
+			case XmlNodeType.Element:
+				switch (newChild.NodeType) {
+				case XmlNodeType.Attribute:
+				case XmlNodeType.Document:
+				case XmlNodeType.DocumentType:
+				case XmlNodeType.Entity:
+				case XmlNodeType.Notation:
+				case XmlNodeType.XmlDeclaration:
+					throw new ArgumentException ("Cannot insert specified type of node as a child of this node.");
+				}
+				break;
+			}
+
+			if (IsReadOnly)
+				throw new ArgumentException ("The specified node is readonly.");
+
+			if (newChild.OwnerDocument != ownerDoc)
+				throw new ArgumentException ("Can't append a node created by another document.");
 
-				if (refChild != null && newChild.OwnerDocument != refChild.OwnerDocument)
-						throw new ArgumentException ("argument nodes are on the different documents.");
+			if (refChild != null && newChild.OwnerDocument != refChild.OwnerDocument)
+					throw new ArgumentException ("argument nodes are on the different documents.");
 
-				// This check is done by MS.NET 1.0, but isn't done for MS.NET 1.1. 
-				// Skip this check in the meantime...
+			// This check is done by MS.NET 1.0, but isn't done for MS.NET 1.1. 
+			// Skip this check in the meantime...
 //				if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement))
 //					throw new XmlException ("multiple document element not allowed.");
 
-				// checking validity finished. then appending...
+			// checking validity finished. then appending...
 
-				return insertBeforeIntern (newChild, refChild);
-			} 
-			else
-				throw new InvalidOperationException (String.Format ("current node {0} is not allowed to have any children.", NodeType));
+			return insertBeforeIntern (newChild, refChild);
 		}
 
 		internal XmlNode insertBeforeIntern (XmlNode newChild, XmlNode refChild)

+ 46 - 27
mcs/class/System.XML/System.Xml/XmlTextWriter.cs

@@ -20,6 +20,7 @@ namespace System.Xml
 	public class XmlTextWriter : XmlWriter
 	{
 		#region Fields
+		const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
 
 		TextWriter w;
 		bool nullEncoding = false;
@@ -51,6 +52,11 @@ namespace System.Xml
 		ArrayList newAttributeNamespaces = new ArrayList ();
 		bool checkMultipleAttributes = false;
 
+		XmlNamespaceManager namespaceManager = new XmlNamespaceManager (new NameTable ());
+		string savingAttributeValue = String.Empty;
+		bool saveAttributeValue = false;
+		string savedAttributePrefix;
+
 		#endregion
 
 		#region Constructors
@@ -227,8 +233,12 @@ namespace System.Xml
 			{
 				string ans = (string)newAttributeNamespaces[n];
 				string aprefix = namespaceManager.LookupPrefix (ans);
-				string formatXmlns = String.Format (" xmlns:{0}={1}{2}{1}", aprefix, quoteChar, ans);
-				w.Write(formatXmlns);
+
+				if(checkMultipleAttributes && !writtenAttributes.Contains ("xmlns:" + aprefix))
+				{
+					string formatXmlns = String.Format (" xmlns:{0}={1}{2}{1}", aprefix, quoteChar, ans);
+					w.Write(formatXmlns);
+				}
 			}
 			newAttributeNamespaces.Clear ();
 		}
@@ -312,11 +322,11 @@ namespace System.Xml
 
 		public override void WriteBase64 (byte[] buffer, int index, int count)
 		{
-			CheckState ();
-
-			if (!openAttribute) {
-				IndentingOverriden = true;
-				CloseStartElement ();
+			CheckState ();
+
+			if (!openAttribute) {
+				IndentingOverriden = true;
+				CloseStartElement ();
 			}
 
 			w.Write (Convert.ToBase64String (buffer, index, count));
@@ -411,6 +421,15 @@ namespace System.Xml
 			w.Write ("{0}", quoteChar);
 
 			openAttribute = false;
+
+			if (saveAttributeValue) {
+				// add namespace
+				namespaceManager.AddNamespace (
+					savedAttributePrefix, savingAttributeValue);
+				saveAttributeValue = false;
+				savedAttributePrefix = String.Empty;
+				savingAttributeValue = String.Empty;
+			}
 		}
 
 		public override void WriteEndDocument ()
@@ -536,8 +555,8 @@ namespace System.Xml
 			if ((prefix == "xmlns") && (localName.ToLower ().StartsWith ("xml")))
 				throw new ArgumentException ("Prefixes beginning with \"xml\" (regardless of whether the characters are uppercase, lowercase, or some combination thereof) are reserved for use by XML: " + prefix + ":" + localName);
 
-			if ((prefix == "xmlns") && (ns != "http://www.w3.org/2000/xmlns/"))
-				throw new ArgumentException ("The 'xmlns' attribute is bound to the reserved namespace 'http://www.w3.org/2000/xmlns/'");
+			if ((prefix == "xmlns") && (ns != XmlnsNamespace))
+				throw new ArgumentException (String.Format ("The 'xmlns' attribute is bound to the reserved namespace '{0}'", XmlnsNamespace));
 
 			CheckState ();
 
@@ -557,7 +576,7 @@ namespace System.Xml
 			{
 				string existingPrefix = namespaceManager.LookupPrefix (ns);
 
-				if (existingPrefix == null) 
+				if (existingPrefix == null) 
 				{
 					newAttributeNamespaces.Add (ns);
 					if (prefix == "" || namespaceManager.LookupNamespace (prefix) != null)
@@ -565,7 +584,7 @@ namespace System.Xml
 					namespaceManager.AddNamespace (prefix, ns);
 				}
 
-				if (prefix == String.Empty && ns != "http://www.w3.org/2000/xmlns/")
+				if (prefix == String.Empty && ns != XmlnsNamespace)
 					prefix = (existingPrefix == null) ?
 						String.Empty : existingPrefix;
 			}
@@ -591,16 +610,11 @@ namespace System.Xml
 			attributeWrittenForElement = true;
 			ws = WriteState.Attribute;
 
-/*			This is managed in WriteAttributeString.
-			
-			if (prefix == String.Empty && localName == "xmlns") {
-				if (namespaceManager.LookupNamespace (prefix) == null)
-					namespaceManager.AddNamespace (prefix, ns);
-			} else if (prefix == "xmlns") {
-				if (namespaceManager.LookupNamespace (localName) == null)
-					namespaceManager.AddNamespace (localName, ns);
+			if (prefix == "xmlns" || prefix == String.Empty && localName == "xmlns") {
+				saveAttributeValue = true;
+				savedAttributePrefix = (prefix == "xmlns") ? localName : String.Empty;
+				savingAttributeValue = String.Empty;
 			}
-			*/
 		}
 
 		public override void WriteStartDocument ()
@@ -698,6 +712,11 @@ namespace System.Xml
 				throw new InvalidOperationException ("Token content in state Prolog would result in an invalid XML document.");
 
 			WriteStringInternal (text, true);
+
+			// MS.NET (1.0) saves attribute value only at WriteString.
+			if (saveAttributeValue)
+				// In most cases it will be called one time, so simply use string + string.
+				savingAttributeValue += text;
 		}
 
 		private string NormalizeAttributeString (string value)
@@ -770,13 +789,13 @@ namespace System.Xml
 			foreach (char c in ws) {
 				if ((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n'))
 					throw new ArgumentException ();
-			}
-
-			CheckState ();
-
-			if (!openAttribute) {
-				IndentingOverriden = true;
-				CloseStartElement ();
+			}
+
+			CheckState ();
+
+			if (!openAttribute) {
+				IndentingOverriden = true;
+				CloseStartElement ();
 			}
 
 			w.Write (ws);

+ 34 - 24
mcs/class/System.XML/System.Xml/XmlWriter.cs

@@ -16,7 +16,6 @@ namespace System.Xml
 		#region Fields
 
 		protected WriteState ws = WriteState.Start;
-		protected XmlNamespaceManager namespaceManager = new XmlNamespaceManager (new NameTable ());
 
 		#endregion
 
@@ -44,7 +43,25 @@ namespace System.Xml
 
 		public abstract string LookupPrefix (string ns);
 
-		[MonoTODO("DTDs must be implemented to use 'defattr' parameter.")]
+		private void WriteAttribute (XmlReader reader, bool defattr)
+		{
+			if (!defattr && reader.IsDefault)
+				return;
+
+			WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
+			while (reader.ReadAttributeValue ()) {
+				switch (reader.NodeType) {
+				case XmlNodeType.Text:
+					WriteString (reader.Value);
+					break;
+				case XmlNodeType.EntityReference:
+					WriteEntityRef (reader.Name);
+					break;
+				}
+			}
+			WriteEndAttribute ();
+		}
+
 		public virtual void WriteAttributes (XmlReader reader, bool defattr)
 		{
 			if(reader == null)
@@ -52,14 +69,19 @@ namespace System.Xml
 
 			switch (reader.NodeType) {
 			case XmlNodeType.XmlDeclaration:
+				WriteAttributeString ("version", reader ["version"]);
+				if (reader ["encoding"] != null)
+					WriteAttributeString ("encoding", reader ["encoding"]);
+				if (reader ["standalone"] != null)
+					WriteAttributeString ("standalone", reader ["standalone"]);
+				break;
 			case XmlNodeType.Element:
 				if (reader.MoveToFirstAttribute ())
 					goto case XmlNodeType.Attribute;
 				break;
 			case XmlNodeType.Attribute:
 				do {
-					// FIXME: use ReadAttributeValue () for strictly write EntityReference.
-					WriteAttributeString (reader.Prefix, reader.LocalName, reader.NamespaceURI, reader.Value);
+					WriteAttribute (reader, defattr);
 				} while (reader.MoveToNextAttribute ());
 				break;
 			default:
@@ -79,28 +101,15 @@ namespace System.Xml
 
 		public void WriteAttributeString (string prefix, string localName, string ns, string value)
 		{
+			// In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
+			// (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
 			if ((prefix == "xmlns") || (prefix == "" && localName == "xmlns"))
-			{
-				if (ns == null)  ns = "http://www.w3.org/2000/xmlns/";
-				if (prefix == "xmlns" && namespaceManager.HasNamespace (localName))
-				  	return;
-			}
+				if (ns == null)
+					ns = "http://www.w3.org/2000/xmlns/";
 
 			WriteStartAttribute (prefix, localName, ns);
 			WriteString (value);
 			WriteEndAttribute ();
-
-			if ((prefix == "xmlns") || (localName == "xmlns")) 
-			{
-				if (prefix == "xmlns")
-				{
-					if (value == string.Empty) throw new ArgumentException ("Cannot use a prefix with an empty namespace");
-					namespaceManager.AddNamespace (localName, value);
-				}
-				else
-					namespaceManager.AddNamespace ("", value);
-			}
-			
 		}
 
 		public abstract void WriteBase64 (byte[] buffer, int index, int count);
@@ -145,7 +154,6 @@ namespace System.Xml
 
 		public abstract void WriteNmToken (string name);
 
-		[MonoTODO("needs to test")]
 		public virtual void WriteNode (XmlReader reader, bool defattr)
 		{
 			if (reader == null)
@@ -208,14 +216,16 @@ namespace System.Xml
 			case XmlNodeType.EndEntity:
 				break;
 			case XmlNodeType.XmlDeclaration:
+				// FIXME: It seems different from MS way, but I have 
+				// no other idea to write start document statefully.
 				string st = reader.GetAttribute ("standalone");
 				if (st != null && st != String.Empty)
-                                        WriteStartDocument (st.ToLower () == "yes");
+					WriteStartDocument (st.ToLower () == "yes");
 				else
 					WriteStartDocument ();
 				break;
 			default:
-				throw new NotImplementedException ();
+				throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
 			}
 			reader.Read ();
 		}