2004-02-06 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlAttributeCollection.cs
index 203d8d33f155a323a3128be24c00dc9740a70b67..d1d63a20f9db73a030537532a3193aecf0a35e61 100644 (file)
@@ -3,28 +3,39 @@
 //
 // Author:
 //   Jason Diamond (jason@injektilo.org)
+//   Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
 //
 // (C) 2002 Jason Diamond  http://injektilo.org/
+// (C) 2002 Atsushi Enomoto
 //
 
 using System;
 using System.Collections;
+using Mono.Xml;
 
 namespace System.Xml
 {
        public class XmlAttributeCollection : XmlNamedNodeMap, ICollection
        {
+               XmlElement ownerElement;
+               XmlDocument ownerDocument;
+
                internal XmlAttributeCollection (XmlNode parent) : base (parent)
                {
+                       ownerElement = parent as XmlElement;
+                       ownerDocument = parent.OwnerDocument;
+                       if(ownerElement == null)
+                               throw new XmlException ("invalid construction for XmlAttributeCollection.");
                }
 
                bool ICollection.IsSynchronized {
-                       get {
-                               throw new NotImplementedException ();
-                       }
+                       get { return false; }
+               }
+
+               bool IsReadOnly {
+                       get { return ownerElement.IsReadOnly; }
                }
 
-               [MonoTODO]
                [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
                public virtual XmlAttribute this [string name] {
                        get {
@@ -32,7 +43,6 @@ namespace System.Xml
                        }
                }
 
-               [MonoTODO]
                [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
                public virtual XmlAttribute this [int i] {
                        get {
@@ -40,78 +50,241 @@ namespace System.Xml
                        }
                }
 
-               [MonoTODO]
                [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
                public virtual XmlAttribute this [string localName, string namespaceURI] {
                        get {
-                               throw new NotImplementedException ();
+                               return (XmlAttribute) GetNamedItem (localName, namespaceURI);
                        }
                }
 
                object ICollection.SyncRoot {
-                       get {
-                               throw new NotImplementedException ();
-                       }
+                       get { return this; }
                }
 
-               [MonoTODO]
+               
                public virtual XmlAttribute Append (XmlAttribute node) 
                {
-                       throw new NotImplementedException ();
-               }
+                       XmlNode xmlNode = this.SetNamedItem (node);
+                       return node;
+               }       
 
-               [MonoTODO]
-               public void CopyTo (XmlAttribute [] array, int index)
+               public void CopyTo (XmlAttribute[] array, int index)
                {
-                       throw new NotImplementedException ();
+                       // assuming that Nodes is a correct collection.
+                       for(int i=0; i<Nodes.Count; i++)
+                               array [index + i] = Nodes [i] as XmlAttribute;
                }
 
-               [MonoTODO]
                void ICollection.CopyTo (Array array, int index)
                {
-                       throw new NotImplementedException ();
+                       // assuming that Nodes is a correct collection.
+                       array.CopyTo (Nodes.ToArray (typeof(XmlAttribute)), index);
                }
 
-               [MonoTODO]
                public virtual XmlAttribute InsertAfter (XmlAttribute newNode, XmlAttribute refNode)
                {
-                       throw new NotImplementedException ();
+                       if (refNode == null) {
+                               if (Nodes.Count == 0)
+                                       return InsertBefore (newNode, null);
+                               else
+                                       return InsertBefore (newNode, this [0]);
+                       }
+                       for (int i = 0; i < Nodes.Count; i++)
+                               if (refNode == Nodes [i])
+                                       return InsertBefore (newNode, Nodes.Count == i + 1 ? null : this [i + 1]);
+
+                       throw new ArgumentException ("refNode not found in this collection.");
                }
 
-               [MonoTODO]
                public virtual XmlAttribute InsertBefore (XmlAttribute newNode, XmlAttribute refNode)
                {
-                       throw new NotImplementedException ();
+                       if (newNode.OwnerDocument != ownerDocument)
+                               throw new ArgumentException ("different document created this newNode.");
+
+                       ownerDocument.onNodeInserting (newNode, null);
+
+                       int pos = Nodes.Count;
+                       if (refNode != null) {
+                               for (int i = 0; i < Nodes.Count; i++) {
+                                       XmlNode n = Nodes [i] as XmlNode;
+                                       if (n == refNode) {
+                                               pos = i;
+                                               break;
+                                       }
+                               }
+                               if (pos == Nodes.Count)
+                                       throw new ArgumentException ("refNode not found in this collection.");
+                       }
+                       SetNamedItem (newNode, pos, false);
+
+                       ownerDocument.onNodeInserted (newNode, null);
+
+                       return newNode;
                }
 
-               [MonoTODO]
                public virtual XmlAttribute Prepend (XmlAttribute node) 
                {
-                       throw new NotImplementedException ();
+                       return this.InsertAfter (node, null);
                }
 
-               [MonoTODO]
                public virtual XmlAttribute Remove (XmlAttribute node) 
                {
-                       throw new NotImplementedException ();
+                       if (node == null)
+                               throw new ArgumentException ("Specified node is null.");
+                       if (node.OwnerDocument != ownerDocument)
+                               throw new ArgumentException ("Specified node is in a different document.");
+
+                       XmlAttribute retAttr = null;
+                       foreach (XmlAttribute attr in Nodes) {
+                               if (attr == node) {
+                                       retAttr = attr;
+                                       break;
+                               }
+                       }
+
+                       if(retAttr != null) {
+                               ownerDocument.onNodeRemoving (node, null);
+                               base.RemoveNamedItem (retAttr.LocalName, retAttr.NamespaceURI);
+                               RemoveIdenticalAttribute (retAttr);
+                               ownerDocument.onNodeRemoved (node, null);
+                       }
+                       // If it is default, then directly create new attribute.
+                       DTDAttListDeclaration attList = ownerDocument.DocumentType != null ? ownerDocument.DocumentType.DTD.AttListDecls [ownerElement.Name] : null;
+                       DTDAttributeDefinition def = attList != null ? attList [retAttr.Name] : null;
+                       if (def != null && def.DefaultValue != null) {
+                               XmlAttribute attr = ownerDocument.CreateAttribute (
+                                       retAttr.Prefix, retAttr.LocalName, retAttr.NamespaceURI);
+                               attr.Value = def.DefaultValue;
+                               attr.SetDefault ();
+                               this.SetNamedItem (attr);
+                       }
+                       retAttr.SetOwnerElement (null);
+                       return retAttr;
                }
 
-               [MonoTODO]
                public virtual void RemoveAll () 
                {
-                       throw new NotImplementedException ();
+                       int current = 0;
+                       while (current < Count) {
+                               XmlAttribute attr = this [current];
+                               if (!attr.Specified)
+                                       current++;
+                               // It is called for the purpose of event support.
+                               Remove (attr);
+                       }
                }
 
-               [MonoTODO]
                public virtual XmlAttribute RemoveAt (int i) 
                {
-                       throw new NotImplementedException ();
+                       if(Nodes.Count <= i)
+                               return null;
+                       return Remove ((XmlAttribute)Nodes [i]);
                }
 
-               [MonoTODO]
                public override XmlNode SetNamedItem (XmlNode node)
                {
-                       return base.SetNamedItem (node);
+                       if(IsReadOnly)
+                               throw new XmlException ("this AttributeCollection is read only.");
+
+                       XmlAttribute attr = node as XmlAttribute;
+                       if (attr.OwnerElement != null)
+                               throw new InvalidOperationException ("This attribute is already set to another element.");
+
+                       ownerElement.OwnerDocument.onNodeInserting (node, ownerElement);
+
+                       attr.SetOwnerElement (ownerElement);
+                       XmlNode n = AdjustIdenticalAttributes (node as XmlAttribute, base.SetNamedItem (node, -1, false));
+
+                       ownerElement.OwnerDocument.onNodeInserted (node, ownerElement);
+
+                       return n as XmlAttribute;
+               }
+
+               internal void AddIdenticalAttribute ()
+               {
+                       SetIdenticalAttribute (false);
+               }
+
+               internal void RemoveIdenticalAttribute ()
+               {
+                       SetIdenticalAttribute (true);
+               }
+
+               private void SetIdenticalAttribute (bool remove)
+               {
+                       if (ownerElement == null)
+                               return;
+
+                       // Check if new attribute's datatype is ID.
+                       XmlDocumentType doctype = ownerDocument.DocumentType;
+                       if (doctype == null || doctype.DTD == null)
+                               return;
+                       DTDElementDeclaration elem = doctype.DTD.ElementDecls [ownerElement.Name];
+                       foreach (XmlAttribute node in this) {
+                               DTDAttributeDefinition attdef = elem == null ? null : elem.Attributes [node.Name];
+                               if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
+                                       continue;
+
+                               if (remove) {
+                                       if (ownerDocument.GetIdenticalAttribute (node.Value) != null) {
+                                               ownerDocument.RemoveIdenticalAttribute (node.Value);
+                                               return;
+                                       }
+                               } else {
+                                       // adding new identical attribute, but 
+                                       // MS.NET is pity for ID support, so I'm wondering how to correct it...
+                                       if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
+                                               throw new XmlException (String.Format (
+                                                       "ID value {0} already exists in this document.", node.Value));
+                                       ownerDocument.AddIdenticalAttribute (node);
+                                       return;
+                               }
+                       }
+
+               }
+
+               private XmlNode AdjustIdenticalAttributes (XmlAttribute node, XmlNode existing)
+               {
+                       // If owner element is not appended to the document,
+                       // ID table should not be filled.
+                       if (ownerElement == null)
+                               return existing;
+
+                       RemoveIdenticalAttribute (existing);
+
+                       // Check if new attribute's datatype is ID.
+                       XmlDocumentType doctype = node.OwnerDocument.DocumentType;
+                       if (doctype == null || doctype.DTD == null)
+                               return existing;
+                       DTDAttListDeclaration attList = doctype.DTD.AttListDecls [ownerElement.Name];
+                       DTDAttributeDefinition attdef = attList == null ? null : attList.Get (node.Name);
+                       if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
+                               return existing;
+
+                       // adding new identical attribute, but 
+                       // MS.NET is pity for ID support, so I'm wondering how to correct it...
+                       if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
+                               throw new XmlException (String.Format (
+                                       "ID value {0} already exists in this document.", node.Value));
+                       ownerDocument.AddIdenticalAttribute (node);
+
+                       return existing;
+               }
+
+               private XmlNode RemoveIdenticalAttribute (XmlNode existing)
+               {
+                       // If owner element is not appended to the document,
+                       // ID table should not be filled.
+                       if (ownerElement == null)
+                               return existing;
+
+                       if (existing != null) {
+                               // remove identical attribute (if it is).
+                               if (ownerDocument.GetIdenticalAttribute (existing.Value) != null)
+                                       ownerDocument.RemoveIdenticalAttribute (existing.Value);
+                       }
+
+                       return existing;
                }
        }
 }