[Xml] Fix GetAttribute to handle null namespaces properly, add unit test.
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader2.cs
index cfb99f451e685c6ec10d64b58a3d2e7177f9a355..b975f9849e384777d14b81a7282312e9ae175fea 100644 (file)
@@ -40,16 +40,28 @@ using Mono.Xml;
 
 namespace System.Xml
 {
+       // FIXME: this implementation requires somewhat significant change
+       // to expand entities and merge sequential text and entity references
+       // especially to handle whitespace-only entities (such as bug #372839).
+       //
+       // To do it, we have to read ahead the next node when the input is
+       // text, whitespace or significant whitespace and check if the next
+       // node is EntityReference. If it is entref, then it have to merge
+       // the input entity if it is a text.
+       //
+       // This "read ahead" operation may result in proceeding to the next
+       // element, which badly affects IXmlNamespaceResolverimplementation.
+       // So we cannot fix this in simple way.
+
        [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
        public class XmlTextReader : XmlReader,
                IXmlLineInfo, IXmlNamespaceResolver, IHasXmlParserContext
        {
                XmlTextReader entity;
-               XmlTextReaderImpl source;
+               XmlTextReaderImpl source; // dtd2xsd expects this field's existence.
                bool entityInsideAttribute;
                bool insideAttribute;
-               string cachedAttributeValue;
-               bool attributeValueConsumed;
+               Stack<string> entityNameStack;
 
                protected XmlTextReader ()
                {
@@ -130,6 +142,11 @@ namespace System.Xml
                        source = new XmlTextReaderImpl (baseURI, xmlFragment, fragType, context);
                }
 
+               internal XmlTextReader (bool dummy, XmlResolver resolver, string url, XmlNodeType fragType, XmlParserContext context)
+               {
+                       source = new XmlTextReaderImpl (dummy, resolver, url, fragType, context);
+               }
+
                private XmlTextReader (XmlTextReaderImpl entityContainer, bool insideAttribute)
                {
                        source = entityContainer;
@@ -207,8 +224,11 @@ namespace System.Xml
 
                public override XmlNodeType NodeType {
                        get {
-                               if (Current == entity)
-                                       return entity.EOF ? XmlNodeType.EndEntity : entity.NodeType;
+                               if (entity != null)
+                                       return entity.ReadState == ReadState.Initial ?
+                                               source.NodeType :
+                                               entity.EOF ? XmlNodeType.EndEntity :
+                                               entity.NodeType;
                                else
                                        return source.NodeType;
                        }
@@ -281,6 +301,7 @@ namespace System.Xml
                }
 
                internal ConformanceLevel Conformance {
+                       get { return source.Conformance; }
                        set {
                                if (entity != null)
                                        entity.Conformance = value;
@@ -401,6 +422,14 @@ namespace System.Xml
                        source.SetNameTable (nameTable);
                }
 
+               internal void SkipTextDeclaration ()
+               {
+                       if (entity != null)
+                               entity.SkipTextDeclaration ();
+                       else
+                               source.SkipTextDeclaration ();
+               }
+
                // overrides
 
                public override void Close ()
@@ -433,6 +462,11 @@ namespace System.Xml
                        return ((IXmlNamespaceResolver) Current).GetNamespacesInScope (scope);
                }
 
+               IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
+               {
+                       return GetNamespacesInScope (scope);
+               }
+
                public override string LookupNamespace (string prefix)
                {
                        return Current.LookupNamespace (prefix);
@@ -445,10 +479,8 @@ namespace System.Xml
 
                public override void MoveToAttribute (int i)
                {
-                       if (entity != null && entityInsideAttribute) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && entityInsideAttribute)
+                               CloseEntity ();
                        Current.MoveToAttribute (i);
                        insideAttribute = true;
                }
@@ -459,10 +491,8 @@ namespace System.Xml
                                return entity.MoveToAttribute (name);
                        if (!source.MoveToAttribute (name))
                                return false;
-                       if (entity != null && entityInsideAttribute) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && entityInsideAttribute)
+                               CloseEntity ();
                        insideAttribute = true;
                        return true;
                }
@@ -473,20 +503,16 @@ namespace System.Xml
                                return entity.MoveToAttribute (localName, namespaceName);
                        if (!source.MoveToAttribute (localName, namespaceName))
                                return false;
-                       if (entity != null && entityInsideAttribute) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && entityInsideAttribute)
+                               CloseEntity ();
                        insideAttribute = true;
                        return true;
                }
 
                public override bool MoveToElement ()
                {
-                       if (entity != null && entityInsideAttribute) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && entityInsideAttribute)
+                               CloseEntity ();
                        if (!Current.MoveToElement ())
                                return false;
                        insideAttribute = false;
@@ -499,10 +525,8 @@ namespace System.Xml
                                return entity.MoveToFirstAttribute ();
                        if (!source.MoveToFirstAttribute ())
                                return false;
-                       if (entity != null && entityInsideAttribute) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && entityInsideAttribute)
+                               CloseEntity ();
                        insideAttribute = true;
                        return true;
                }
@@ -513,10 +537,8 @@ namespace System.Xml
                                return entity.MoveToNextAttribute ();
                        if (!source.MoveToNextAttribute ())
                                return false;
-                       if (entity != null && entityInsideAttribute) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && entityInsideAttribute)
+                               CloseEntity ();
                        insideAttribute = true;
                        return true;
                }
@@ -525,17 +547,14 @@ namespace System.Xml
                {
                        insideAttribute = false;
 
-                       if (entity != null && (entityInsideAttribute || entity.EOF)) {
-                               entity.Close ();
-                               entity = null;
-                       }
+                       if (entity != null && (entityInsideAttribute || entity.EOF))
+                               CloseEntity ();
                        if (entity != null) {
                                if (entity.Read ())
                                        return true;
                                if (EntityHandling == EntityHandling.ExpandEntities) {
                                        // EndEntity must be skipped
-                                       entity.Close ();
-                                       entity = null;
+                                       CloseEntity ();
                                        return Read ();
                                }
                                else
@@ -556,10 +575,8 @@ namespace System.Xml
                public override bool ReadAttributeValue ()
                {
                        if (entity != null && entityInsideAttribute) {
-                               if (entity.EOF) {
-                                       entity.Close ();
-                                       entity = null;
-                               }
+                               if (entity.EOF)
+                                       CloseEntity ();
                                else {
                                        entity.Read ();
                                        return true; // either success or EndEntity
@@ -576,38 +593,54 @@ namespace System.Xml
                public void ResetState ()
                {
                        if (entity != null)
-                               entity.ResetState ();
+                               CloseEntity ();
                        source.ResetState ();
                }
 
-               public override void ResolveEntity ()
+               public override
+               void ResolveEntity ()
                {
                        if (entity != null)
                                entity.ResolveEntity ();
                        else {
                                if (source.NodeType != XmlNodeType.EntityReference)
                                        throw new InvalidOperationException ("The current node is not an Entity Reference");
-                               XmlTextReaderImpl entReader = 
-                                       ParserContext.Dtd.GenerateEntityContentReader (source.Name, ParserContext);
+                               XmlTextReaderImpl entReader = null;
+                               if (ParserContext.Dtd != null)
+                                       entReader = ParserContext.Dtd.GenerateEntityContentReader (source.Name, ParserContext);
                                if (entReader == null)
                                        throw new XmlException (this as IXmlLineInfo, this.BaseURI, String.Format ("Reference to undeclared entity '{0}'.", source.Name));
+                               if (entityNameStack == null)
+                                       entityNameStack = new Stack<string> ();
+                               else if (entityNameStack.Contains (Name))
+                                       throw new XmlException (String.Format ("General entity '{0}' has an invalid recursive reference to itself.", Name));
+                               entityNameStack.Push (Name);
                                entity = new XmlTextReader (
                                        entReader, insideAttribute);
+                               entity.entityNameStack = entityNameStack;
                                entity.CopyProperties (this);
                        }
                }
 
+               void CloseEntity ()
+               {
+                       entity.Close ();
+                       entity = null;
+                       entityNameStack.Pop ();
+               }
+
                public override void Skip ()
                {
                        base.Skip ();
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public TextReader GetRemainder ()
                {
                        if (entity != null) {
                                entity.Close ();
                                entity = null;
+                               entityNameStack.Pop ();
                        }
                        return source.GetRemainder ();
                }
@@ -617,7 +650,7 @@ namespace System.Xml
                        return true;
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public int ReadBase64 (byte [] buffer, int offset, int length)
                {
                        if (entity != null)
@@ -626,7 +659,7 @@ namespace System.Xml
                                return source.ReadBase64 (buffer, offset, length);
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public int ReadBinHex (byte [] buffer, int offset, int length)
                {
                        if (entity != null)
@@ -635,7 +668,7 @@ namespace System.Xml
                                return source.ReadBinHex (buffer, offset, length);
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public int ReadChars (char [] buffer, int offset, int length)
                {
                        if (entity != null)
@@ -645,7 +678,7 @@ namespace System.Xml
                }
 
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public override int ReadContentAsBase64 (byte [] buffer, int offset, int length)
                {
                        if (entity != null)
@@ -654,7 +687,7 @@ namespace System.Xml
                                return source.ReadContentAsBase64 (buffer, offset, length);
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public override int ReadContentAsBinHex (byte [] buffer, int offset, int length)
                {
                        if (entity != null)
@@ -663,7 +696,7 @@ namespace System.Xml
                                return source.ReadContentAsBinHex (buffer, offset, length);
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public override int ReadElementContentAsBase64 (byte [] buffer, int offset, int length)
                {
                        if (entity != null)
@@ -672,7 +705,7 @@ namespace System.Xml
                                return source.ReadElementContentAsBase64 (buffer, offset, length);
                }
 
-               [MonoTODO ("Check how expanded entity is handled here.")]
+               [MonoTODO] // FIXME: Check how expanded entity is handled here.
                public override int ReadElementContentAsBinHex (byte [] buffer, int offset, int length)
                {
                        if (entity != null)