[Xml] Fix GetAttribute to handle null namespaces properly, add unit test.
[mono.git] / mcs / class / System.XML / System.Xml / DTDObjectModel.cs
index 2930484d1fccc03f224bb7728e7a990aafe5c9c6..bbe073f6f13c6461611e3d586245fff56805aa98 100644 (file)
@@ -6,21 +6,59 @@
 //
 //     (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.Collections;
-using System.Collections.Specialized;
 using System.Globalization;
 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;
-using Mono.Xml.Native;
+#endif
+
+#if NET_2_0
+using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
+#else
+using XmlTextReaderImpl = System.Xml.XmlTextReader;
+#endif
 
 namespace Mono.Xml
 {
-       public class DTDObjectModel
+       internal class DTDObjectModel
        {
+               // This specifies the max number of dependent external entities
+               // per a DTD can consume. A malicious external document server
+               // might send users' document processing server a large number
+               // of external entities.
+               public const int AllowedExternalEntitiesMax = 256;
+
                DTDAutomataFactory factory;
                DTDElementAutomata rootAutomata;
                DTDEmptyAutomata emptyAutomata;
@@ -36,6 +74,8 @@ namespace Mono.Xml
                XmlResolver resolver;
                XmlNameTable nameTable;
 
+               Hashtable externalResources;
+
                string baseURI;
                string name;
                string publicId;
@@ -56,6 +96,7 @@ namespace Mono.Xml
                        notationDecls = new DTDNotationDeclarationCollection (this);
                        factory = new DTDAutomataFactory (this);
                        validationErrors = new ArrayList ();
+                       externalResources = new Hashtable ();
                }
 
                public string BaseURI {
@@ -106,14 +147,31 @@ namespace Mono.Xml
                        get { return linePosition; }
                        set { linePosition = value; }
                }
-               
+
+#if !NET_2_1
+               internal XmlSchema CreateXsdSchema ()
+               {
+                       XmlSchema s = new XmlSchema ();
+                       s.SourceUri = BaseURI;
+                       s.LineNumber = LineNumber;
+                       s.LinePosition = LinePosition;
+                       foreach (DTDElementDeclaration el in ElementDecls.Values)
+                               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
@@ -128,6 +186,10 @@ namespace Mono.Xml
                        set { resolver = value; }
                }
 
+               internal Hashtable ExternalResources {
+                       get { return externalResources; }
+               }
+
                public DTDAutomataFactory Factory {
                        get { return factory; }
                }
@@ -196,9 +258,43 @@ namespace Mono.Xml
                {
                        validationErrors.Add (ex);
                }
+
+               internal string GenerateEntityAttributeText (string entityName)
+               {
+                       DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
+                       if (entity == null)
+                               return null;
+                       return entity.EntityValue;
+               }
+
+               internal XmlTextReaderImpl GenerateEntityContentReader (string entityName, XmlParserContext context)
+               {
+                       DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
+                       if (entity == null)
+                               return null;
+
+                       if (entity.SystemId != null) {
+                               Uri baseUri = entity.BaseURI == String.Empty ? null : new Uri (entity.BaseURI);
+                               Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
+                               return new XmlTextReaderImpl (stream, XmlNodeType.Element, context);
+                       }
+                       else
+                               return new XmlTextReaderImpl (entity.EntityValue, XmlNodeType.Element, context);
+               }
        }
 
-       public class DTDCollectionBase : DictionaryBase
+#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;
 
@@ -211,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; }
                }
@@ -218,9 +340,25 @@ 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
        }
 
-       public class DTDElementDeclarationCollection : DTDCollectionBase
+       internal class DTDElementDeclarationCollection : DTDCollectionBase
        {
 
                public DTDElementDeclarationCollection (DTDObjectModel root) : base (root) {}
@@ -231,28 +369,28 @@ 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);
                }
        }
 
-       public class DTDAttListDeclarationCollection : DTDCollectionBase
+       internal class DTDAttListDeclarationCollection : DTDCollectionBase
        {
                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)
@@ -265,51 +403,51 @@ namespace Mono.Xml
                                                existing.Add (def);
                        } else {
                                decl.SetRoot (Root);
-                               InnerHashtable.Add (name, decl);
+                               BaseAdd (name, decl);
                        }
                }
        }
 
-       public class DTDEntityDeclarationCollection : DTDCollectionBase
+       internal class DTDEntityDeclarationCollection : DTDCollectionBase
        {
                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);
                }
        }
 
-       public class DTDNotationDeclarationCollection : DTDCollectionBase
+       internal class DTDNotationDeclarationCollection : DTDCollectionBase
        {
                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);
                }
        }
 
        // This class contains either ElementName or ChildModels.
-       public class DTDContentModel : DTDNode
+       internal class DTDContentModel : DTDNode
        {
                DTDObjectModel root;
                DTDAutomata compiledAutomata;
@@ -363,6 +501,57 @@ 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) {
+                               XmlSchemaElement el = new XmlSchemaElement ();
+                               SetLineInfo (el);
+                               el.RefName = new XmlQualifiedName (ElementName);
+                               return el;
+                       }
+                       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) {
+                                       XmlSchemaParticle c = cm.CreateXsdParticle ();
+                                       if (c != null)
+                                               gb.Items.Add (c);
+                               }
+                               p = gb;
+                       }
+                       return p;
+               }
+#endif
+
                private DTDAutomata CompileInternal ()
                {
                        if (ElementDecl.IsAny)
@@ -428,10 +617,9 @@ namespace Mono.Xml
                {
                        return l.MakeChoice (r);
                }
-
        }
 
-       public class DTDContentModelCollection
+       internal class DTDContentModelCollection
        {
                ArrayList contentModel = new ArrayList ();
 
@@ -439,6 +627,10 @@ namespace Mono.Xml
                {
                }
 
+               public IList Items {
+                       get { return contentModel; }
+               }
+
                public DTDContentModel this [int i] {
                        get { return contentModel [i] as DTDContentModel; }
                }
@@ -453,7 +645,7 @@ namespace Mono.Xml
                }
        }
 
-       public abstract class DTDNode : IXmlLineInfo
+       internal abstract class DTDNode : IXmlLineInfo
        {
                DTDObjectModel root;
                bool isInternalSubset;
@@ -496,9 +688,23 @@ namespace Mono.Xml
                protected DTDObjectModel Root {
                        get { return root; }
                }
+
+               internal XmlException NotWFError (string message)
+               {
+                       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
        }
 
-       public class DTDElementDeclaration : DTDNode
+       internal class DTDElementDeclaration : DTDNode
        {
                DTDObjectModel root;
                DTDContentModel contentModel;
@@ -544,15 +750,69 @@ namespace Mono.Xml
                                return Root.AttListDecls [Name];
                        }
                }
+
+#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);
+                       }
+                       else if (IsAny)
+                               el.SchemaTypeName = new XmlQualifiedName (
+                                       "anyType", XmlSchema.Namespace);
+                       else {
+                               XmlSchemaComplexType ct = new XmlSchemaComplexType ();
+                               SetLineInfo (ct);
+                               if (Attributes != null)
+                                       foreach (DTDAttributeDefinition a in
+                                               Attributes.Definitions)
+                                               ct.Attributes.Add (a.CreateXsdAttribute ());
+                               if (IsMixedContent)
+                                       ct.IsMixed = true;
+                               ct.Particle = ContentModel.CreateXsdParticle ();
+                               el.SchemaType = ct;
+                       }
+                       */
+                       return el;
+               }
+#endif
        }
 
-       public class DTDAttributeDefinition : DTDNode
+       internal class DTDAttributeDefinition : DTDNode
        {
                string name;
                XmlSchemaDatatype datatype;
-               ArrayList enumeratedLiterals = new ArrayList ();
+               ArrayList enumeratedLiterals;
                string unresolvedDefault;
-               ArrayList enumeratedNotations = new ArrayList ();
+               ArrayList enumeratedNotations;
                DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
                string resolvedDefaultValue;
                string resolvedNormalizedDefaultValue;
@@ -582,11 +842,19 @@ namespace Mono.Xml
                // Then I decided to use string ArrayList for enumerated values,
                // and unresolved string value for DefaultValue.
                public ArrayList EnumeratedAttributeDeclaration {
-                       get { return this.enumeratedLiterals; }
+                       get {
+                               if (enumeratedLiterals == null)
+                                       enumeratedLiterals = new ArrayList ();
+                               return this.enumeratedLiterals;
+                       }
                }
 
                public ArrayList EnumeratedNotations {
-                       get { return this.enumeratedNotations; }
+                       get {
+                               if (enumeratedNotations == null)
+                                       enumeratedNotations = new ArrayList ();
+                               return this.enumeratedNotations;
+                       }
                }
 
                public string DefaultValue {
@@ -600,11 +868,17 @@ namespace Mono.Xml
                public string NormalizedDefaultValue {
                        get {
                                if (resolvedNormalizedDefaultValue == null) {
-                                       object o = Datatype.ParseValue (ComputeDefaultValue (), null, null);
-                                       resolvedNormalizedDefaultValue = 
-                                               (o is string []) ? 
-                                               String.Join (" ", (string []) o) :
-                                               o.ToString ();
+                                       string s = ComputeDefaultValue ();
+                                       try {
+                                               object o = Datatype.ParseValue (s, null, null);
+                                               resolvedNormalizedDefaultValue = 
+                                                       (o is string []) ? 
+                                                       String.Join (" ", (string []) o) :
+                                                       o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
+                                       } catch (Exception) {
+                                               // This is for non-error-reporting reader
+                                               resolvedNormalizedDefaultValue = Datatype.Normalize (s);
+                                       }
                                }
                                return resolvedNormalizedDefaultValue;
                        }
@@ -623,7 +897,70 @@ namespace Mono.Xml
                        }
                }
 
-               private string ComputeDefaultValue ()
+#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;
+                       if (enumeratedNotations != null && enumeratedNotations.Count > 0) {
+                               qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace);
+                               enumeration = enumeratedNotations;
+                       }
+                       else if (enumeratedLiterals != null)
+                               enumeration = enumeratedLiterals;
+                       else {
+                               switch (Datatype.TokenizedType) {
+                               case XmlTokenizedType.ID:
+                                       qname = new XmlQualifiedName ("ID", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.IDREF:
+                                       qname = new XmlQualifiedName ("IDREF", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.IDREFS:
+                                       qname = new XmlQualifiedName ("IDREFS", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.ENTITY:
+                                       qname = new XmlQualifiedName ("ENTITY", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.ENTITIES:
+                                       qname = new XmlQualifiedName ("ENTITIES", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.NMTOKEN:
+                                       qname = new XmlQualifiedName ("NMTOKEN", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.NMTOKENS:
+                                       qname = new XmlQualifiedName ("NMTOKENS", XmlSchema.Namespace); break;
+                               case XmlTokenizedType.NOTATION:
+                                       qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace); break;
+                               }
+                       }
+
+                       if (enumeration != null) {
+                               XmlSchemaSimpleType st = new XmlSchemaSimpleType ();
+                               SetLineInfo (st);
+                               XmlSchemaSimpleTypeRestriction r =
+                                       new XmlSchemaSimpleTypeRestriction ();
+                               SetLineInfo (r);
+                               r.BaseTypeName = qname;
+                               if (enumeratedNotations != null) {
+                                       foreach (string name in enumeratedNotations) {
+                                               XmlSchemaEnumerationFacet f =
+                                                       new XmlSchemaEnumerationFacet ();
+                                               SetLineInfo (f);
+                                               r.Facets.Add (f);
+                                               f.Value = name;
+                                       }
+                               }
+                               st.Content = r;
+                       }
+                       else if (qname != XmlQualifiedName.Empty)
+                               a.SchemaTypeName = qname;
+                       return a;
+               }
+#endif
+
+               internal string ComputeDefaultValue ()
                {
                        if (UnresolvedDefaultValue == null)
                                return null;
@@ -645,7 +982,7 @@ namespace Mono.Xml
                                        }
                                        else
                                                spec = value.Substring (next + 2, semicolon - next - 2);
-                                       sb.Append ((char) int.Parse (spec, style));
+                                       sb.Append ((char) int.Parse (spec, style, CultureInfo.InvariantCulture));
                                } else {
                                        sb.Append (value.Substring (pos, next - 1));
                                        string name = value.Substring (next + 1, semicolon - 2);
@@ -666,7 +1003,7 @@ namespace Mono.Xml
 
        }
 
-       public class DTDAttListDeclaration : DTDNode
+       internal class DTDAttListDeclaration : DTDNode
        {
                string name;
                Hashtable attributeOrders = new Hashtable ();
@@ -704,7 +1041,7 @@ namespace Mono.Xml
                                return null;
                }
 
-               public ICollection Definitions {
+               public IList Definitions {
                        get { return attributes; }
                }
 
@@ -724,16 +1061,24 @@ namespace Mono.Xml
                }
        }
 
-       public class DTDEntityBase : DTDNode
+       internal class DTDEntityBase : DTDNode
        {
                string name;
                string publicId;
                string systemId;
                string literalValue;
                string replacementText;
+               string uriString;
+               Uri absUri;
                bool isInvalid;
-               Exception loadException;
+//             Exception loadException;
                bool loadFailed;
+               XmlResolver resolver;
+
+               protected DTDEntityBase (DTDObjectModel root)
+               {
+                       SetRoot (root);
+               }
 
                internal bool IsInvalid {
                        get { return isInvalid; }
@@ -770,53 +1115,77 @@ namespace Mono.Xml
                        set { replacementText = value; }
                }
 
-               public void Resolve (XmlResolver resolver)
+               public XmlResolver XmlResolver {
+                       set { resolver = value; }
+               }
+
+               public string ActualUri {
+                       get {
+                               if (uriString == null) {
+                                       if (resolver == null || SystemId == null || SystemId.Length == 0)
+                                               uriString = BaseURI;
+                                       else {
+                                               Uri baseUri = null;
+                                               try {
+                                                       if (BaseURI != null && BaseURI.Length > 0)
+                                                               baseUri = new Uri (BaseURI);
+                                               } catch (UriFormatException) {
+                                               }
+
+                                               absUri = resolver.ResolveUri (baseUri, SystemId);
+                                               uriString = absUri != null ? absUri.ToString () : String.Empty;
+                                       }
+                               }
+                               return uriString;
+                       }
+               }
+
+               public void Resolve ()
                {
-                       if (resolver == null || SystemId == null || SystemId.Length == 0) {
+                       if (ActualUri == String.Empty) {
                                LoadFailed = true;
                                LiteralEntityValue = String.Empty;
                                return;
                        }
 
-                       Uri baseUri = null;
+                       if (Root.ExternalResources.ContainsKey (ActualUri))
+                               LiteralEntityValue = (string) Root.ExternalResources [ActualUri];
+                       Stream s = null;
                        try {
-                               if (BaseURI != null && BaseURI.Length > 0)
-                                       baseUri = new Uri (BaseURI);
-                       } catch (UriFormatException) {
-                       }
-
-                       Uri absUri = resolver.ResolveUri (baseUri, SystemId);
-                       string absPath = absUri.ToString ();
-
-                       try {
-                               Stream s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
-                               XmlTextReader xtr = new XmlTextReader (s);
+                               s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
+                               XmlTextReaderImpl xtr = new XmlTextReaderImpl (ActualUri, s, Root.NameTable);
                                // Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
-                               this.BaseURI = absPath;
                                LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
-                       } catch (Exception ex) {
-                               loadException = ex;
+
+                               Root.ExternalResources.Add (ActualUri, LiteralEntityValue);
+                               if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
+                                       throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
+
+                       } catch (Exception) {
+//                             loadException = ex;
                                LiteralEntityValue = String.Empty;
                                LoadFailed = true;
-//                             throw new XmlException (this, "Cannot resolve external entity. URI is " + absPath + " .");
+//                             throw NotWFError ("Cannot resolve external entity. URI is " + ActualUri + " .");
+                       } finally {
+                               if (s != null)
+                                       s.Close ();
                        }
                }
        }
 
-       public class DTDEntityDeclaration : DTDEntityBase
+       internal class DTDEntityDeclaration : DTDEntityBase
        {
                string entityValue;
                string notationName;
 
-               StringCollection ReferencingEntities = new StringCollection ();
+               ArrayList ReferencingEntities = new ArrayList ();
 
                bool scanned;
                bool recursed;
                bool hasExternalReference;
 
-               internal DTDEntityDeclaration (DTDObjectModel root)
+               internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
                {
-                       this.SetRoot (root);
                }
 
                public string NotationName {
@@ -827,7 +1196,7 @@ namespace Mono.Xml
                public bool HasExternalReference {
                        get {
                                if (!scanned)
-                                       ScanEntityValue (new StringCollection ());
+                                       ScanEntityValue (new ArrayList ());
                                return hasExternalReference;
                        }
                }
@@ -844,23 +1213,21 @@ namespace Mono.Xml
                                        if (NotationName != null)
                                                entityValue = "";
                                        else if (SystemId == null || SystemId == String.Empty) {
-                                               // FIXME: Isn't it an error??
                                                entityValue = ReplacementText;
-//                                             entityValue = LiteralEntityValue;
                                                if (entityValue == null)
                                                        entityValue = String.Empty;
                                        } else {
                                                entityValue = ReplacementText;
                                        }
                                        // Check illegal recursion.
-                                       ScanEntityValue (new StringCollection ());
+                                       ScanEntityValue (new ArrayList ());
                                }
                                return entityValue;
                        }
                }
 
                // It returns whether the entity contains references to external entities.
-               public void ScanEntityValue (StringCollection refs)
+               public void ScanEntityValue (ArrayList refs)
                {
                        // To modify this code, beware nesting between this and EntityValue.
                        string value = EntityValue;
@@ -868,13 +1235,13 @@ namespace Mono.Xml
                                hasExternalReference = true;
 
                        if (recursed)
-                               throw new XmlException ("Entity recursion was found.");
+                               throw NotWFError ("Entity recursion was found.");
                        recursed = true;
 
                        if (scanned) {
                                foreach (string referenced in refs)
                                        if (this.ReferencingEntities.Contains (referenced))
-                                               throw new XmlException (String.Format (
+                                               throw NotWFError (String.Format (
                                                        "Nested entity was found between {0} and {1}",
                                                        referenced, Name));
                                recursed = false;
@@ -893,12 +1260,9 @@ namespace Mono.Xml
                                                break;
                                        string name = value.Substring (start, i - start);
                                        if (name.Length == 0)
-                                               throw new XmlException (this as IXmlLineInfo, "Entity reference name is missing.");
+                                               throw NotWFError ("Entity reference name is missing.");
                                        if (name [0] == '#')
                                                break;  // character reference
-                                       // FIXME: Should be checked, but how to handle entity for ENTITY attribute?
-//                                     if (!XmlChar.IsName (name))
-//                                             throw new XmlException (this as IXmlLineInfo, "Invalid entity reference name.");
                                        if (XmlChar.GetPredefinedEntity (name) >= 0)
                                                break;  // predefined reference
 
@@ -922,15 +1286,18 @@ 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;
-                       entityValue = value;
                        recursed = false;
                }
        }
 
-       public class DTDNotationDeclaration : DTDNode
+       internal class DTDNotationDeclaration : DTDNode
        {
                string name;
                string localName;
@@ -969,7 +1336,7 @@ namespace Mono.Xml
                }
        }
 
-       public class DTDParameterEntityDeclarationCollection
+       internal class DTDParameterEntityDeclarationCollection
        {
                Hashtable peDecls = new Hashtable ();
                DTDObjectModel root;
@@ -1001,18 +1368,21 @@ namespace Mono.Xml
                }
        }
 
-       public class DTDParameterEntityDeclaration : DTDEntityBase
+       internal class DTDParameterEntityDeclaration : DTDEntityBase
        {
+               internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
+               {
+               }
        }
 
-       public enum DTDContentOrderType
+       internal enum DTDContentOrderType
        {
                None,
                Seq,
                Or
        }
 
-       public enum DTDAttributeOccurenceType
+       internal enum DTDAttributeOccurenceType
        {
                None,
                Required,
@@ -1020,7 +1390,7 @@ namespace Mono.Xml
                Fixed
        }
 
-       public enum DTDOccurence
+       internal enum DTDOccurence
        {
                One,
                Optional,