[Xml] Fix GetAttribute to handle null namespaces properly, add unit test.
[mono.git] / mcs / class / System.XML / System.Xml / DTDObjectModel.cs
index 5cbebbd8913d6d9e45a540bdd62071da4270589e..bbe073f6f13c6461611e3d586245fff56805aa98 100644 (file)
@@ -34,7 +34,14 @@ using System.IO;
 using System.Text;
 using System.Xml;
 using System.Xml.Schema;
+#if NET_2_0
+using System.Collections.Generic;
+#endif
+#if NET_2_1
+using XmlSchemaException = System.Xml.XmlException;
+#else
 using Mono.Xml.Schema;
+#endif
 
 #if NET_2_0
 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
@@ -141,6 +148,7 @@ namespace Mono.Xml
                        set { linePosition = value; }
                }
 
+#if !NET_2_1
                internal XmlSchema CreateXsdSchema ()
                {
                        XmlSchema s = new XmlSchema ();
@@ -151,14 +159,19 @@ namespace Mono.Xml
                                s.Items.Add (el.CreateXsdElement ());
                        return s;
                }
+#endif
 
                public string ResolveEntity (string name)
                {
                        DTDEntityDeclaration decl = EntityDecls [name] 
                                as DTDEntityDeclaration;
                        if (decl == null) {
+#if NET_2_1
+                               AddError (new XmlSchemaException (String.Format ("Required entity was not found: {0}", name), null, this.LineNumber, this.LinePosition));
+#else
                                AddError (new XmlSchemaException ("Required entity was not found.",
                                        this.LineNumber, this.LinePosition, null, this.BaseURI, null));
+#endif
                                return " ";
                        }
                        else
@@ -246,7 +259,6 @@ namespace Mono.Xml
                        validationErrors.Add (ex);
                }
 
-#if NET_2_0
                internal string GenerateEntityAttributeText (string entityName)
                {
                        DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
@@ -269,9 +281,19 @@ namespace Mono.Xml
                        else
                                return new XmlTextReaderImpl (entity.EntityValue, XmlNodeType.Element, context);
                }
-#endif
        }
 
+#if NET_2_0
+       class DictionaryBase : List<KeyValuePair<string,DTDNode>>
+       {
+               public IEnumerable<DTDNode> Values {
+                       get {
+                               foreach (KeyValuePair<string,DTDNode> p in this)
+                                       yield return p.Value;
+                       }
+               }
+       }
+#endif
        internal class DTDCollectionBase : DictionaryBase
        {
                DTDObjectModel root;
@@ -285,6 +307,32 @@ namespace Mono.Xml
                        get { return root; }
                }
 
+#if NET_2_0
+               public DictionaryBase InnerHashtable {
+                       get { return this; }
+               }
+
+               protected void BaseAdd (string name, DTDNode value)
+               {
+                       base.Add (new KeyValuePair<string,DTDNode> (name, value));
+               }
+
+               public bool Contains (string key)
+               {
+                       foreach (KeyValuePair<string,DTDNode> p in this)
+                               if (p.Key == key)
+                                       return true;
+                       return false;
+               }
+
+               protected object BaseGet (string name)
+               {
+                       foreach (KeyValuePair<string,DTDNode> p in this)
+                               if (p.Key == name)
+                                       return p.Value;
+                       return null;
+               }
+#else
                public ICollection Keys {
                        get { return InnerHashtable.Keys; }
                }
@@ -292,6 +340,22 @@ namespace Mono.Xml
                public ICollection Values {
                        get { return InnerHashtable.Values; }
                }
+
+               protected void BaseAdd (string name, object value)
+               {
+                       InnerHashtable.Add (name, value);
+               }
+
+               public bool Contains (string key)
+               {
+                       return InnerHashtable.Contains (key);
+               }
+
+               protected object BaseGet (string name)
+               {
+                       return InnerHashtable [name];
+               }
+#endif
        }
 
        internal class DTDElementDeclarationCollection : DTDCollectionBase
@@ -305,19 +369,19 @@ namespace Mono.Xml
 
                public DTDElementDeclaration Get (string name)
                {
-                       return InnerHashtable [name] as DTDElementDeclaration;
+                       return BaseGet (name) as DTDElementDeclaration;
                }
 
                public void Add (string name, DTDElementDeclaration decl)
                {
-                       if (InnerHashtable.Contains (name)) {
+                       if (Contains (name)) {
                                Root.AddError (new XmlSchemaException (String.Format (
                                        "Element declaration for {0} was already added.",
                                        name), null));
                                return;
                        }
                        decl.SetRoot (Root);
-                       InnerHashtable.Add (name, decl);
+                       BaseAdd (name, decl);
                }
        }
 
@@ -326,7 +390,7 @@ namespace Mono.Xml
                public DTDAttListDeclarationCollection (DTDObjectModel root) : base (root) {}
 
                public DTDAttListDeclaration this [string name] {
-                       get { return InnerHashtable [name] as DTDAttListDeclaration; }
+                       get { return BaseGet (name) as DTDAttListDeclaration; }
                }
 
                public void Add (string name, DTDAttListDeclaration decl)
@@ -339,7 +403,7 @@ namespace Mono.Xml
                                                existing.Add (def);
                        } else {
                                decl.SetRoot (Root);
-                               InnerHashtable.Add (name, decl);
+                               BaseAdd (name, decl);
                        }
                }
        }
@@ -349,17 +413,17 @@ namespace Mono.Xml
                public DTDEntityDeclarationCollection (DTDObjectModel root) : base (root) {}
 
                public DTDEntityDeclaration this [string name] {
-                       get { return InnerHashtable [name] as DTDEntityDeclaration; }
+                       get { return BaseGet (name) as DTDEntityDeclaration; }
                }
 
                public void Add (string name, DTDEntityDeclaration decl)
                {
-                       if (InnerHashtable [name] != null)
+                       if (Contains (name))
                                throw new InvalidOperationException (String.Format (
                                        "Entity declaration for {0} was already added.",
                                        name));
                        decl.SetRoot (Root);
-                       InnerHashtable.Add (name, decl);
+                       BaseAdd (name, decl);
                }
        }
 
@@ -368,17 +432,17 @@ namespace Mono.Xml
                public DTDNotationDeclarationCollection (DTDObjectModel root) : base (root) {}
 
                public DTDNotationDeclaration this [string name] {
-                       get { return InnerHashtable [name] as DTDNotationDeclaration; }
+                       get { return BaseGet (name) as DTDNotationDeclaration; }
                }
 
                public void Add (string name, DTDNotationDeclaration decl)
                {
-                       if (InnerHashtable [name] != null)
+                       if (Contains (name))
                                throw new InvalidOperationException (String.Format (
                                        "Notation declaration for {0} was already added.",
                                        name));
                        decl.SetRoot (Root);
-                       InnerHashtable.Add (name, decl);
+                       BaseAdd (name, decl);
                }
        }
 
@@ -437,7 +501,29 @@ namespace Mono.Xml
                        return compiledAutomata;
                }
 
+#if !NET_2_1
                internal XmlSchemaParticle CreateXsdParticle ()
+               {
+                       XmlSchemaParticle p = CreateXsdParticleCore ();
+                       if (p == null)
+                               return null;
+
+                       switch (Occurence) {
+                       case DTDOccurence.Optional:
+                               p.MinOccurs = 0;
+                               break;
+                       case DTDOccurence.OneOrMore:
+                               p.MaxOccursString = "unbounded";
+                               break;
+                       case DTDOccurence.ZeroOrMore:
+                               p.MinOccurs = 0;
+                               p.MaxOccursString = "unbounded";
+                               break;
+                       }
+                       return p;
+               }
+
+               XmlSchemaParticle CreateXsdParticleCore ()
                {
                        XmlSchemaParticle p = null;
                        if (ElementName != null) {
@@ -445,31 +531,26 @@ namespace Mono.Xml
                                SetLineInfo (el);
                                el.RefName = new XmlQualifiedName (ElementName);
                                return el;
-                       } else {
+                       }
+                       else if (ChildModels.Count == 0)
+                               return null;
+                       else {
                                XmlSchemaGroupBase gb = 
                                        (OrderType == DTDContentOrderType.Seq) ?
                                                (XmlSchemaGroupBase)
                                                new XmlSchemaSequence () :
                                                new XmlSchemaChoice ();
                                SetLineInfo (gb);
-                               foreach (DTDContentModel cm in ChildModels.Items)
-                                       gb.Items.Add (cm.CreateXsdParticle ());
+                               foreach (DTDContentModel cm in ChildModels.Items) {
+                                       XmlSchemaParticle c = cm.CreateXsdParticle ();
+                                       if (c != null)
+                                               gb.Items.Add (c);
+                               }
                                p = gb;
                        }
-                       switch (Occurence) {
-                       case DTDOccurence.Optional:
-                               p.MinOccurs = 0;
-                               break;
-                       case DTDOccurence.OneOrMore:
-                               p.MaxOccursString = "unbounded";
-                               break;
-                       case DTDOccurence.ZeroOrMore:
-                               p.MinOccurs = 0;
-                               p.MaxOccursString = "unbounded";
-                               break;
-                       }
                        return p;
                }
+#endif
 
                private DTDAutomata CompileInternal ()
                {
@@ -613,12 +694,14 @@ namespace Mono.Xml
                        return new XmlException (this as IXmlLineInfo, BaseURI, message);
                }
 
+#if !NET_2_1
                public void SetLineInfo (XmlSchemaObject obj)
                {
                        obj.SourceUri = BaseURI;
                        obj.LineNumber = LineNumber;
                        obj.LinePosition = LinePosition;
                }
+#endif
        }
 
        internal class DTDElementDeclaration : DTDNode
@@ -668,11 +751,36 @@ namespace Mono.Xml
                        }
                }
 
+#if !NET_2_1
                internal XmlSchemaElement CreateXsdElement ()
                {
                        XmlSchemaElement el = new XmlSchemaElement ();
                        SetLineInfo (el);
                        el.Name = Name;
+
+                       XmlSchemaComplexType ct = new XmlSchemaComplexType ();
+                       el.SchemaType = ct;
+                       if (Attributes != null) {
+                               SetLineInfo (ct);
+                               foreach (DTDAttributeDefinition a in
+                                       Attributes.Definitions)
+                                       ct.Attributes.Add (a.CreateXsdAttribute ());
+                       }
+                       if (IsEmpty)
+                               ; // nothing to do
+                       else if (IsAny) {
+                               XmlSchemaAny any = new XmlSchemaAny ();
+                               any.MinOccurs = 0;
+                               any.MaxOccursString = "unbounded";
+                               ct.Particle = any;
+                       }
+                       else {
+                               if (IsMixedContent)
+                                       ct.IsMixed = true;
+                               ct.Particle = ContentModel.CreateXsdParticle ();
+                       }
+
+                       /*
                        if (IsEmpty) {
                                el.SchemaType = new XmlSchemaComplexType ();
                                SetLineInfo (el.SchemaType);
@@ -692,8 +800,10 @@ namespace Mono.Xml
                                ct.Particle = ContentModel.CreateXsdParticle ();
                                el.SchemaType = ct;
                        }
+                       */
                        return el;
                }
+#endif
        }
 
        internal class DTDAttributeDefinition : DTDNode
@@ -787,12 +897,15 @@ namespace Mono.Xml
                        }
                }
 
+#if !NET_2_1
                internal XmlSchemaAttribute CreateXsdAttribute ()
                {
                        XmlSchemaAttribute a = new XmlSchemaAttribute ();
                        SetLineInfo (a);
                        a.Name = Name;
                        a.DefaultValue = resolvedNormalizedDefaultValue;
+                       if (OccurenceType != DTDAttributeOccurenceType.Required)
+                               a.Use = XmlSchemaUse.Optional;
 
                        XmlQualifiedName qname = XmlQualifiedName.Empty;
                        ArrayList enumeration = null;
@@ -839,11 +952,13 @@ namespace Mono.Xml
                                                f.Value = name;
                                        }
                                }
+                               st.Content = r;
                        }
                        else if (qname != XmlQualifiedName.Empty)
                                a.SchemaTypeName = qname;
                        return a;
                }
+#endif
 
                internal string ComputeDefaultValue ()
                {
@@ -1046,7 +1161,7 @@ namespace Mono.Xml
                                if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
                                        throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
 
-                       } catch (Exception ex) {
+                       } catch (Exception) {
 //                             loadException = ex;
                                LiteralEntityValue = String.Empty;
                                LoadFailed = true;
@@ -1171,8 +1286,12 @@ namespace Mono.Xml
                                }
                        }
                        if (start != 0)
+#if NET_2_1
+                               Root.AddError (new XmlSchemaException (this, this.BaseURI, "Invalid reference character '&' is specified."));
+#else
                                Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
                                        this.LineNumber, this.LinePosition, null, this.BaseURI, null));
+#endif
                        scanned = true;
                        recursed = false;
                }