2004-10-18 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
index c23fedd3b8478b9e4c8f5ec349af9f77497accb9..f1d5b59e8b04e5f2364e7c63c64d26bd4fe4522d 100644 (file)
@@ -9,6 +9,27 @@
 // (C) 2001, 2002 Jason Diamond  http://injektilo.org/
 //
 
+//
+// 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.
+//
+
 // FIXME:
 //
 //   Some thought needs to be given to performance.
@@ -19,6 +40,7 @@
 
 using System;
 using System.Collections;
+using System.Globalization;
 using System.IO;
 using System.Security.Policy;
 using System.Text;
@@ -27,7 +49,12 @@ using Mono.Xml;
 
 namespace System.Xml
 {
+#if NET_2_0
+       public class XmlTextReader : XmlReader,
+               IXmlLineInfo, IXmlNamespaceResolver
+#else
        public class XmlTextReader : XmlReader, IXmlLineInfo
+#endif
        {
                #region Constructors
 
@@ -131,6 +158,24 @@ namespace System.Xml
                        get { return parserContext.BaseURI; }
                }
 
+#if NET_2_0
+               public override bool CanResolveEntity {
+                       get { return true; }
+               }
+
+#endif
+
+               internal bool CharacterChecking {
+                       get { return checkCharacters && normalization; }
+                       set { checkCharacters = value; }
+               }
+
+               // for XmlReaderSettings.CloseInput support
+               internal bool CloseInput {
+                       get { return closeInput; }
+                       set { closeInput = value; }
+               }
+
                public override int Depth
                {
                        get {
@@ -150,24 +195,19 @@ namespace System.Xml
 #if NET_2_0
                [MonoTODO]
                public EntityHandling EntityHandling {
-                       get { throw new NotImplementedException (); }
+                       get { return entityHandling; }
+                       set { entityHandling = value; }
                }
 #endif
 
-               public override bool EOF
-               {
-                       get
-                       {
-                               return
-                                       readState == ReadState.EndOfFile ||
-                                       readState == ReadState.Closed;
-                       }
+               public override bool EOF {
+                       get { return readState == ReadState.EndOfFile; }
                }
 
 #if NET_2_0
                [MonoTODO]
-               public override Evidence [] Evidences {
-                       get { return base.Evidences; }
+               public override Evidence Evidence {
+                       get { return base.Evidence; }
                }
 #endif
 
@@ -253,8 +293,6 @@ namespace System.Xml
                }
 
 #if NET_2_0
-
-               [MonoTODO ("Not documented in .NET SDK")]
                public bool ProhibitDtd {
                        get { return prohibitDtd; }
                        set { prohibitDtd = value; }
@@ -269,6 +307,12 @@ namespace System.Xml
                        get { return readState; }
                }
 
+#if NET_2_0
+               public override XmlReaderSettings Settings {
+                       get { return base.Settings; }
+               }
+#endif
+
                public override string Value {
                        get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
                }
@@ -301,7 +345,7 @@ namespace System.Xml
                        cursorToken.Clear ();
                        currentToken.Clear ();
                        attributeCount = 0;
-                       if (reader != null)
+                       if (closeInput && reader != null)
                                reader.Close ();
                }
 
@@ -348,6 +392,13 @@ namespace System.Xml
                        return attributeTokens [idx].Value;
                }
 
+#if NET_2_0
+               public IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
+               {
+                       return parserContext.NamespaceManager.GetNamespacesInScope (scope);
+               }
+#endif
+
                public TextReader GetRemainder ()
                {
                        if (peekCharsIndex == peekCharsLength)
@@ -355,7 +406,11 @@ namespace System.Xml
                        return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
                }
 
+#if NET_2_0
+               public bool HasLineInfo ()
+#else
                bool IXmlLineInfo.HasLineInfo ()
+#endif
                {
                        return true;
                }
@@ -365,11 +420,27 @@ namespace System.Xml
                        return LookupNamespace (prefix, false);
                }
 
-               internal string LookupNamespace (string prefix, bool atomizedName)
+#if NET_2_0
+               public override string LookupNamespace (string prefix, bool atomizedName)
+#else
+               internal override string LookupNamespace (string prefix, bool atomizedName)
+#endif
                {
                        return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
                }
 
+#if NET_2_0
+               string IXmlNamespaceResolver.LookupPrefix (string ns)
+               {
+                       return LookupPrefix (ns, false);
+               }
+
+               public string LookupPrefix (string ns, bool atomizedName)
+               {
+                       return parserContext.NamespaceManager.LookupPrefix (ns, atomizedName);
+               }
+#endif
+
                public override void MoveToAttribute (int i)
                {
                        if (i >= attributeCount)
@@ -509,6 +580,14 @@ namespace System.Xml
                                return false;
                }
 
+               private int SkipIgnorableBase64Chars (char [] chars, int charsLength, int i)
+               {
+                       while (chars [i] == '=' || XmlChar.IsWhitespace (chars [i]))
+                               if (charsLength == ++i)
+                                       break;
+                       return i;
+               }
+
                public int ReadBase64 (byte [] buffer, int offset, int length)
                {
                        if (offset < 0)
@@ -545,7 +624,10 @@ namespace System.Xml
 
                        byte b = 0;
                        byte work = 0;
-                       for (int i = 0; i < charsLength - 3; i += 4) {
+                       bool loop = true;
+                       for (int i = 0; i < charsLength - 3; i++) {
+                               if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
+                                       break;
                                b = (byte) (GetBase64Byte (chars [i]) << 2);
                                if (bufIndex < bufLast)
                                        buffer [bufIndex] = b;
@@ -555,9 +637,11 @@ namespace System.Xml
                                        base64Cache [0] = b;
                                }
                                // charsLength mod 4 might not equals to 0.
-                               if (i + 1 == charsLength)
+                               if (++i == charsLength)
+                                       break;
+                               if ((i = SkipIgnorableBase64Chars (chars, charsLength, i))  == charsLength)
                                        break;
-                               b = GetBase64Byte (chars [i + 1]);
+                               b = GetBase64Byte (chars [i]);
                                work = (byte) (b >> 4);
                                if (bufIndex < bufLast) {
                                        buffer [bufIndex] += work;
@@ -576,9 +660,11 @@ namespace System.Xml
                                        base64Cache [1] = work;
                                }
 
-                               if (i + 2 == charsLength)
+                               if (++i == charsLength)
                                        break;
-                               b = GetBase64Byte (chars [i + 2]);
+                               if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
+                                       break;
+                               b = GetBase64Byte (chars [i]);
                                work = (byte) (b >> 2);
                                if (bufIndex < bufLast) {
                                        buffer [bufIndex] += work;
@@ -595,9 +681,11 @@ namespace System.Xml
                                                base64CacheStartsAt = 2;
                                        base64Cache [2] = work;
                                }
-                               if (i + 3 == charsLength)
+                               if (++i == charsLength)
+                                       break;
+                               if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
                                        break;
-                               work = GetBase64Byte (chars [i + 3]);
+                               work = GetBase64Byte (chars [i]);
                                if (bufIndex < bufLast) {
                                        buffer [bufIndex] += work;
                                        bufIndex++;
@@ -630,7 +718,12 @@ namespace System.Xml
                        return ReadCharsInternal (buffer, offset, length);
                }
 
-#if NET_1_1
+#if NET_2_0
+               public override string ReadString ()
+               {
+                       return ReadStringInternal ();
+               }
+#elif NET_1_1
 #else
                public override string ReadInnerXml ()
                {
@@ -650,9 +743,78 @@ namespace System.Xml
 
                public void ResetState ()
                {
+                       throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment.");
                        Init ();
                }
 
+#if NET_2_0
+               [MonoTODO]
+               public override bool ReadValueAsBoolean ()
+               {
+                       return base.ReadValueAsBoolean ();
+               }
+
+               [MonoTODO]
+               public override DateTime ReadValueAsDateTime ()
+               {
+                       return base.ReadValueAsDateTime ();
+               }
+
+               [MonoTODO]
+               public override decimal ReadValueAsDecimal ()
+               {
+                       return base.ReadValueAsDecimal ();
+               }
+
+               [MonoTODO]
+               public override double ReadValueAsDouble ()
+               {
+                       return base.ReadValueAsDouble ();
+               }
+
+               [MonoTODO]
+               public override int ReadValueAsInt32 ()
+               {
+                       return base.ReadValueAsInt32 ();
+               }
+
+               [MonoTODO]
+               public override long ReadValueAsInt64 ()
+               {
+                       return base.ReadValueAsInt64 ();
+               }
+
+               [MonoTODO]
+               public override ICollection ReadValueAsList ()
+               {
+                       return base.ReadValueAsList ();
+               }
+
+               [MonoTODO]
+               public override float ReadValueAsSingle ()
+               {
+                       return base.ReadValueAsSingle ();
+               }
+
+               [MonoTODO]
+               public override string ReadValueAsString ()
+               {
+                       return ReadString ();
+               }
+
+               [MonoTODO]
+               public override object ReadValueAs (Type type)
+               {
+                       return base.ReadValueAs (type);
+               }
+
+               [MonoTODO]
+               public override object ReadValueAs (Type type, IXmlNamespaceResolver resolver)
+               {
+                       return base.ReadValueAs (type, resolver);
+               }
+#endif
+
                public override void ResolveEntity ()
                {
                        // XmlTextReader does not resolve entities.
@@ -955,9 +1117,11 @@ namespace System.Xml
                private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
                private XmlResolver resolver = new XmlUrlResolver ();
                private bool normalization = false;
-#if NET_2_0
+
+               private bool checkCharacters;
                private bool prohibitDtd = false;
-#endif
+               private bool closeInput = true;
+               private EntityHandling entityHandling; // 2.0
 
                private void Init ()
                {
@@ -1000,7 +1164,7 @@ namespace System.Xml
                                peekChars = new char [peekCharCapacity];
 
                        line = 1;
-                       column = 0;
+                       column = 1;
                        currentTagLength = 0;
 
                        currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
@@ -1010,6 +1174,15 @@ namespace System.Xml
 
                        shouldSkipUntilEndTag = false;
                        base64CacheStartsAt = -1;
+
+                       checkCharacters = true;
+#if NET_2_0
+                       if (Settings != null)
+                               checkCharacters = Settings.CheckCharacters;
+#endif
+                       prohibitDtd = false;
+                       closeInput = true;
+                       entityHandling = EntityHandling.ExpandCharEntities;
                }
 
                private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
@@ -1037,9 +1210,11 @@ namespace System.Xml
 
                        Init ();
 
+                       reader = fragment;
+
                        switch (fragType) {
                        case XmlNodeType.Attribute:
-                               fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", "&quot;"));
+                               reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", "&quot;"));
                                break;
                        case XmlNodeType.Element:
                                currentState = XmlNodeType.Element;
@@ -1050,10 +1225,31 @@ namespace System.Xml
                        default:
                                throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
                        }
+               }
 
-                       reader = fragment;
+#if NET_2_0
+               [MonoTODO ("Test")]
+               internal ConformanceLevel Conformance {
+                       set {
+                               if (value == ConformanceLevel.Fragment) {
+                                       currentState = XmlNodeType.Element;
+                                       allowMultipleRoot = true;
+                               }
+                       }
                }
 
+               internal void AdjustLineInfoOffset (int lineNumberOffset, int linePositionOffset)
+               {
+                       line += lineNumberOffset;
+                       column += linePositionOffset;
+               }
+
+               internal void SetNameTable (XmlNameTable nameTable)
+               {
+                       parserContext.NameTable = nameTable;
+               }
+#endif
+
                // Use this method rather than setting the properties
                // directly so that all the necessary properties can
                // be changed in harmony with each other. Maybe the
@@ -1108,8 +1304,11 @@ namespace System.Xml
                                        return -1;
                                return PeekChar ();
                        }
-                       else
-                               return peekChars [peekCharsIndex];
+                       else {
+                               char c = peekChars [peekCharsIndex];
+                               if (c != 0) return c;
+                               else return -1;
+                       }
                }
 
                private int ReadChar ()
@@ -1126,6 +1325,8 @@ namespace System.Xml
                        if (ch == '\n') {
                                line++;
                                column = 1;
+                       } else if (ch == 0) {
+                               return -1;
                        } else {
                                column++;
                        }
@@ -1167,24 +1368,8 @@ namespace System.Xml
                        if (returnEntityReference)
                                SetEntityReferenceProperties ();
                        else {
-                               switch (PeekChar ()) {
-                               case '<':
-                                       ReadChar ();
-                                       ReadTag ();
-                                       break;
-                               case '\r': goto case ' ';
-                               case '\n': goto case ' ';
-                               case '\t': goto case ' ';
-                               case ' ':
-                                       if (whitespaceHandling == WhitespaceHandling.All ||
-                                               whitespaceHandling == WhitespaceHandling.Significant)
-                                               ReadWhitespace ();
-                                       else {
-                                               SkipWhitespace ();
-                                               return ReadContent ();
-                                       }
-                                       break;
-                               case -1:
+                               int c = PeekChar ();
+                               if (c == -1) {
                                        readState = ReadState.EndOfFile;
                                        ClearValueBuffer ();
                                        SetProperties (
@@ -1198,9 +1383,28 @@ namespace System.Xml
                                                throw new XmlException ("unexpected end of file. Current depth is " + depth);
 
                                        return false;
-                               default:
-                                       ReadText (true);
-                                       break;
+                               } else {
+                                       switch ((char) c) {
+                                       case '<':
+                                               ReadChar ();
+                                               ReadTag ();
+                                               break;
+                                       case '\r': goto case ' ';
+                                       case '\n': goto case ' ';
+                                       case '\t': goto case ' ';
+                                       case ' ':
+                                               if (whitespaceHandling == WhitespaceHandling.All ||
+                                                       whitespaceHandling == WhitespaceHandling.Significant)
+                                                       ReadWhitespace ();
+                                               else {
+                                                       SkipWhitespace ();
+                                                       return ReadContent ();
+                                               }
+                                               break;
+                                       default:
+                                               ReadText (true);
+                                               break;
+                                       }
                                }
                        }
                        return this.ReadState != ReadState.EndOfFile;
@@ -1263,6 +1467,9 @@ namespace System.Xml
 
                        parserContext.NamespaceManager.PushScope ();
 
+                       currentLinkedNodeLineNumber = line;
+                       currentLinkedNodeLinePosition = column;
+
                        string name = ReadName ();
                        if (currentState == XmlNodeType.EndElement)
                                throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
@@ -1363,6 +1570,9 @@ namespace System.Xml
                                throw new XmlException (this as IXmlLineInfo,
                                        "End tag cannot appear in this state.");
 
+                       currentLinkedNodeLineNumber = line;
+                       currentLinkedNodeLinePosition = column;
+
                        string name = ReadName ();
                        if (elementNameStackPos == 0)
                                throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
@@ -1505,9 +1715,15 @@ namespace System.Xml
                                        ch = ReadReference (false);
                                        if (returnEntityReference) // Returns -1 if char validation should not be done
                                                break;
-                               }
-                               else {
-                                       if (XmlChar.IsInvalid (ch))
+                               } else if (normalization && ch == '\r') {
+                                       ReadChar ();
+                                       ch = ReadChar ();
+                                       if (ch != '\n')
+                                               // append '\n' instead of '\r'.
+                                               AppendValueChar ('\n');
+                                       // and in case of "\r\n", discard '\r'.
+                               } else {
+                                       if (CharacterChecking && XmlChar.IsInvalid (ch))
                                                throw new XmlException (this, "Not allowed character was found.");
                                        ch = ReadChar ();
                                }
@@ -1575,7 +1791,7 @@ namespace System.Xml
                                                value = (value << 4) + ch - 'a' + 10;
                                        else
                                                throw new XmlException (this as IXmlLineInfo,
-                                                       String.Format (
+                                                       String.Format (CultureInfo.InvariantCulture, 
                                                                "invalid hexadecimal digit: {0} (#x{1:X})",
                                                                (char) ch,
                                                                ch));
@@ -1588,7 +1804,7 @@ namespace System.Xml
                                                value = value * 10 + ch - '0';
                                        else
                                                throw new XmlException (this as IXmlLineInfo,
-                                                       String.Format (
+                                                       String.Format (CultureInfo.InvariantCulture, 
                                                                "invalid decimal digit: {0} (#x{1:X})",
                                                                (char) ch,
                                                                ch));
@@ -1598,9 +1814,9 @@ namespace System.Xml
                        ReadChar (); // ';'
 
                        // There is no way to save surrogate pairs...
-                       if (normalization && XmlChar.IsInvalid (value))
+                       if (CharacterChecking && XmlChar.IsInvalid (value))
                                throw new XmlException (this as IXmlLineInfo,
-                                       "Referenced character was not allowed in XML.");
+                                       "Referenced character was not allowed in XML. Normalization is " + normalization + ", checkCharacters = " + checkCharacters);
                        return value;
                }
 
@@ -1763,7 +1979,7 @@ namespace System.Xml
                                        if (PeekChar () == '#') {
                                                ReadChar ();
                                                ch = ReadCharacterReference ();
-                                               if (normalization && XmlChar.IsInvalid (ch))
+                                               if (CharacterChecking && XmlChar.IsInvalid (ch))
                                                        throw new XmlException (this as IXmlLineInfo,
                                                                "Not allowed character was found.");
                                                AppendValueChar (ch);
@@ -1788,7 +2004,7 @@ namespace System.Xml
                                                AppendValueChar (predefined);
                                        break;
                                default:
-                                       if (normalization && XmlChar.IsInvalid (ch))
+                                       if (CharacterChecking && XmlChar.IsInvalid (ch))
                                                throw new XmlException (this, "Invalid character was found.");
                                        AppendValueChar (ch);
                                        break;
@@ -1832,7 +2048,7 @@ namespace System.Xml
                        if (target == "xml") {
                                ReadXmlDeclaration ();
                                return;
-                       } else if (target.ToLower () == "xml")
+                       } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml")
                                throw new XmlException (this as IXmlLineInfo,
                                        "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
 
@@ -1854,7 +2070,7 @@ namespace System.Xml
                                        break;
                                }
 
-                               if (normalization && XmlChar.IsInvalid (ch))
+                               if (CharacterChecking && XmlChar.IsInvalid (ch))
                                        throw new XmlException (this, "Invalid character was found.");
                                AppendValueChar (ch);
                        }
@@ -1934,7 +2150,7 @@ namespace System.Xml
                                        ReadChar ();
                        }
                        if (new string (peekChars, 2, 4) != "xml ") {
-                               if (new string (peekChars, 2, 3).ToLower () == "xml") {
+                               if (new string (peekChars, 2, 3).ToLower (CultureInfo.InvariantCulture) == "xml") {
                                        throw new XmlException (this as IXmlLineInfo,
                                                "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
                                }
@@ -2099,7 +2315,15 @@ namespace System.Xml
                                                skip = true;
                                        }
                                }
-                               if (normalization && XmlChar.IsInvalid (ch))
+                               if (normalization && ch == '\r') {
+                                       ch = PeekChar ();
+                                       if (ch != '\n')
+                                               // append '\n' instead of '\r'.
+                                               AppendValueChar ('\n');
+                                       // otherwise, discard '\r'.
+                                       continue;
+                               }
+                               if (CharacterChecking && XmlChar.IsInvalid (ch))
                                        throw new XmlException (this, "Invalid character was found.");
 
                                AppendValueChar (ch);
@@ -2118,6 +2342,9 @@ namespace System.Xml
                // the leading '<!DOCTYPE'.
                private void ReadDoctypeDecl ()
                {
+                       if (prohibitDtd)
+                               throw new XmlException (this as IXmlLineInfo,
+                                       "Document Type Declaration (DTD) is prohibited in this XML.");
                        switch (currentState) {
                        case XmlNodeType.DocumentType:
                        case XmlNodeType.Element:
@@ -2290,10 +2517,14 @@ namespace System.Xml
                                case -1:
                                        throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
                                case '<':
-                                       if (State == DtdInputState.InsideDoubleQuoted ||
-                                               State == DtdInputState.InsideSingleQuoted)
+                                       switch (State) {
+                                       case DtdInputState.InsideDoubleQuoted:
+                                       case DtdInputState.InsideSingleQuoted:
+                                       case DtdInputState.Comment:
                                                continue;       // well-formed
-                                       switch (ReadChar ()) {
+                                       }
+                                       int c = ReadChar ();
+                                       switch (c) {
                                        case '?':
                                                stateStack.Push (DtdInputState.PI);
                                                break;
@@ -2328,7 +2559,7 @@ namespace System.Xml
                                                }
                                                break;
                                        default:
-                                               throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
+                                               throw new XmlException (this as IXmlLineInfo, String.Format ("unexpected '<{0}'.", (char) c));
                                        }
                                        break;
                                case '\'':
@@ -2355,9 +2586,7 @@ namespace System.Xml
                                                stateStack.Pop ();
                                                break;
                                        case DtdInputState.InsideDoubleQuoted:
-                                               continue;
                                        case DtdInputState.InsideSingleQuoted:
-                                               continue; // well-formed
                                        case DtdInputState.Comment:
                                                continue;
                                        default:
@@ -2440,7 +2669,7 @@ namespace System.Xml
                {
                        int ch = PeekChar ();
                        if (!XmlChar.IsFirstNameChar (ch))
-                               throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
+                               throw new XmlException (this as IXmlLineInfo,String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
 
                        nameLength = 0;
 
@@ -2461,7 +2690,7 @@ namespace System.Xml
 
                        if (ch != expected) {
                                throw new XmlException (this as IXmlLineInfo,
-                                       String.Format (
+                                       String.Format (CultureInfo.InvariantCulture, 
                                                "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
                                                (char) expected,
                                                expected,
@@ -2484,7 +2713,7 @@ namespace System.Xml
                                if (i < 0x21 && XmlChar.IsWhitespace (i))
                                        continue;
                                if (c != i)
-                                       throw new XmlException (this, String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
+                                       throw new XmlException (this, String.Format (CultureInfo.InvariantCulture, "Expected {0}, but found {1} [{2}]", c, (char) i, i));
                                break;
                        }
                }
@@ -2526,6 +2755,8 @@ namespace System.Xml
                        return;
                }
 
+               // Since ReadBase64() is processed for every 4 chars, it does
+               // not handle '=' here.
                private byte GetBase64Byte (char ch)
                {
                        switch (ch) {
@@ -2533,8 +2764,6 @@ namespace System.Xml
                                return 62;
                        case '/':
                                return 63;
-                       case '=':
-                               return 0;
                        default:
                                if (ch >= 'A' && ch <= 'Z')
                                        return (byte) (ch - 'A');
@@ -2555,8 +2784,6 @@ namespace System.Xml
                                return 0;
                        }
 
-                       shouldSkipUntilEndTag = true;
-
                        if (offset < 0)
                                throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
                        else if (length < 0)
@@ -2567,6 +2794,8 @@ namespace System.Xml
                        if (NodeType != XmlNodeType.Element)
                                return 0;
 
+                       shouldSkipUntilEndTag = true;
+
                        int bufIndex = offset;
                        for (int i = 0; i < length; i++) {
                                int c = PeekChar ();
@@ -2585,9 +2814,9 @@ namespace System.Xml
                                                depth++;
                                                depthUp = false;
                                        }
-                                       ReadEndTag();
+                                       ReadEndTag ();
                                        shouldSkipUntilEndTag = false;
-                                       Read ();
+                                       Read (); // move to the next node
                                        return i;
                                default:
                                        ReadChar ();