2004-10-18 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
index 6122f984cb17d407a4136343619ea0d532494715..f1d5b59e8b04e5f2364e7c63c64d26bd4fe4522d 100644 (file)
@@ -40,7 +40,7 @@
 
 using System;
 using System.Collections;
-using System.Globalization;\r
+using System.Globalization;
 using System.IO;
 using System.Security.Policy;
 using System.Text;
@@ -49,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
 
@@ -153,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 {
@@ -172,18 +195,13 @@ 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
@@ -289,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; }
                }
@@ -321,7 +345,7 @@ namespace System.Xml
                        cursorToken.Clear ();
                        currentToken.Clear ();
                        attributeCount = 0;
-                       if (reader != null)
+                       if (closeInput && reader != null)
                                reader.Close ();
                }
 
@@ -368,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)
@@ -375,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;
                }
@@ -385,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)
@@ -529,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)
@@ -565,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;
@@ -575,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;
@@ -596,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;
@@ -615,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++;
@@ -650,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 ()
                {
@@ -670,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.
@@ -976,7 +1118,10 @@ namespace System.Xml
                private XmlResolver resolver = new XmlUrlResolver ();
                private bool normalization = false;
 
+               private bool checkCharacters;
                private bool prohibitDtd = false;
+               private bool closeInput = true;
+               private EntityHandling entityHandling; // 2.0
 
                private void Init ()
                {
@@ -1019,7 +1164,7 @@ namespace System.Xml
                                peekChars = new char [peekCharCapacity];
 
                        line = 1;
-                       column = 0;
+                       column = 1;
                        currentTagLength = 0;
 
                        currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
@@ -1029,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)
@@ -1056,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;
@@ -1069,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
@@ -1127,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 ()
@@ -1145,6 +1325,8 @@ namespace System.Xml
                        if (ch == '\n') {
                                line++;
                                column = 1;
+                       } else if (ch == 0) {
+                               return -1;
                        } else {
                                column++;
                        }
@@ -1285,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");
@@ -1385,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");
@@ -1527,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 ();
                                }
@@ -1620,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;
                }
 
@@ -1785,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);
@@ -1810,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;
@@ -1876,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);
                        }
@@ -2121,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);
@@ -2315,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;
@@ -2353,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 '\'':
@@ -2380,9 +2586,7 @@ namespace System.Xml
                                                stateStack.Pop ();
                                                break;
                                        case DtdInputState.InsideDoubleQuoted:
-                                               continue;
                                        case DtdInputState.InsideSingleQuoted:
-                                               continue; // well-formed
                                        case DtdInputState.Comment:
                                                continue;
                                        default:
@@ -2551,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) {
@@ -2558,8 +2764,6 @@ namespace System.Xml
                                return 62;
                        case '/':
                                return 63;
-                       case '=':
-                               return 0;
                        default:
                                if (ch >= 'A' && ch <= 'Z')
                                        return (byte) (ch - 'A');
@@ -2580,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)
@@ -2592,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 ();
@@ -2610,9 +2814,9 @@ namespace System.Xml
                                                depth++;
                                                depthUp = false;
                                        }
-                                       ReadEndTag();
+                                       ReadEndTag ();
                                        shouldSkipUntilEndTag = false;
-                                       Read ();
+                                       Read (); // move to the next node
                                        return i;
                                default:
                                        ReadChar ();