New test.
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
index 954fd4b56f86e41260c455092918a692836b9815..af67f4f3fbebf9d86747954e88daffe37faa034d 100644 (file)
@@ -7,7 +7,7 @@
 //   Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
 //
 // (C) 2001, 2002 Jason Diamond  http://injektilo.org/
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
+//#define USE_NAME_BUFFER
+
+//
+// Optimization TODOs:
+//
+//     - support PushbackChar() which reverts one character read.
+//             - ReadTextReader() should always keep one pushback buffer
+//               as pushback safety net.
+//     - Replace (peek,read) * n -> read * n + pushback
+//
 
 using System;
 using System.Collections;
@@ -117,8 +127,12 @@ namespace System.Xml
 
                // This is used in XmlReader.Create() to indicate that string
                // argument is uri, not an xml fragment.
-               internal XmlTextReader (bool dummy, string url, XmlNodeType fragType, XmlParserContext context)
+               internal XmlTextReader (bool dummy, XmlResolver resolver, string url, XmlNodeType fragType, XmlParserContext context)
                {
+                       if (resolver == null)
+                               resolver = new XmlUrlResolver ();
+
+                       this.XmlResolver = resolver;
                        string uriString;
                        Stream stream = GetStreamFromUrl (url, out uriString);
                        this.InitializeContext (uriString, context, new XmlStreamReader (stream), fragType);
@@ -425,11 +439,16 @@ namespace System.Xml
                {
                        return parserContext.NamespaceManager.GetNamespacesInScope (scope);
                }
+
+               IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
+               {
+                       return GetNamespacesInScope (scope);
+               }
 #endif
 
                public TextReader GetRemainder ()
                {
-                       if (peekCharsIndex == peekCharsLength)
+                       if (peekCharsLength < 0)
                                return reader;
                        return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
                }
@@ -540,9 +559,13 @@ namespace System.Xml
 
                public override bool Read ()
                {
+                       curNodePeekIndex = peekCharsIndex;
+                       preserveCurrentTag = true;
+
                        if (startNodeType == XmlNodeType.Attribute) {
                                if (currentAttribute == 0)
                                        return false;   // already read.
+                               SkipTextDeclaration ();
                                ClearAttributes ();
                                IncrementAttributeToken ();
                                ReadAttributeValueTokens ('"');
@@ -551,6 +574,8 @@ namespace System.Xml
                                readState = ReadState.Interactive;
                                return true;
                        }
+                       if (readState == ReadState.Initial && currentState == XmlNodeType.Element)
+                               SkipTextDeclaration ();
 
                        if (Binary != null)
                                Binary.Reset ();
@@ -647,23 +672,6 @@ namespace System.Xml
                        return ReadCharsInternal (buffer, offset, length);
                }
 
-#if NET_1_0
-               public override string ReadInnerXml ()
-               {
-                       return ReadInnerXmlInternal ();
-               }
-
-               public override string ReadOuterXml ()
-               {
-                       return ReadOuterXmlInternal ();
-               }
-
-               public override string ReadString ()
-               {
-                       return ReadStringInternal ();
-               }
-#endif
-
                public void ResetState ()
                {
                        throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment.");
@@ -677,7 +685,7 @@ namespace System.Xml
                }
 
 #if NET_2_0
-               [MonoTODO ("Implement for performance reason")]
+               [MonoTODO] // FIXME: Implement, for performance improvement
                public override void Skip ()
                {
                        base.Skip ();
@@ -687,6 +695,7 @@ namespace System.Xml
 
                #region Internals
                // Parsed DTD Objects
+               // Note that thgis property must be kept since dtd2xsd uses it.
                internal DTDObjectModel DTD {
                        get { return parserContext.Dtd; }
                }
@@ -853,7 +862,21 @@ namespace System.Xml
 
                private bool popScope;
 
-               private string [] elementNames;
+               struct TagName
+               {
+                       public TagName (string n, string l, string p)
+                       {
+                               Name = n;
+                               LocalName = l;
+                               Prefix = p;
+                       }
+
+                       public readonly string Name;
+                       public readonly string LocalName;
+                       public readonly string Prefix;
+               }
+
+               private TagName [] elementNames;
                int elementNameStackPos;
 
                private bool allowMultipleRoot;
@@ -863,22 +886,21 @@ namespace System.Xml
                private bool returnEntityReference;
                private string entityReferenceName;
 
+#if USE_NAME_BUFFER
                private char [] nameBuffer;
                private int nameLength;
                private int nameCapacity;
                private const int initialNameCapacity = 32;
+#endif
 
                private StringBuilder valueBuffer;
 
-               private char [] currentTagBuffer;
-               private int currentTagLength;
-               private int currentTagCapacity;
-               private const int initialCurrentTagCapacity = 256;
-
                private TextReader reader;
                private char [] peekChars;
                private int peekCharsIndex;
                private int peekCharsLength;
+               private int curNodePeekIndex;
+               private bool preserveCurrentTag;
                private const int peekCharCapacity = 1024;
 
                private int line;
@@ -911,6 +933,9 @@ namespace System.Xml
                private bool closeInput = true;
                private EntityHandling entityHandling; // 2.0
 
+               private NameTable whitespacePool;
+               private char [] whitespaceCache;
+
                private XmlException NotWFError (string message)
                {
                        return new XmlException (this as IXmlLineInfo, BaseURI, message);
@@ -932,27 +957,26 @@ namespace System.Xml
                        depthUp = false;
 
                        popScope = allowMultipleRoot = false;
-                       elementNames = new string [10];
+                       elementNames = new TagName [10];
                        elementNameStackPos = 0;
 
                        isStandalone = false;
                        returnEntityReference = false;
                        entityReferenceName = String.Empty;
 
+#if USE_NAME_BUFFER
                        nameBuffer = new char [initialNameCapacity];
                        nameLength = 0;
                        nameCapacity = initialNameCapacity;
+#endif
 
                        valueBuffer = new StringBuilder ();
 
-                       currentTagBuffer = new char [initialCurrentTagCapacity];
-                       currentTagLength = 0;
-                       currentTagCapacity = initialCurrentTagCapacity;
-
                        peekCharsIndex = 0;
-                       peekCharsLength = 0;
                        if (peekChars == null)
                                peekChars = new char [peekCharCapacity];
+                       peekCharsLength = -1;
+                       curNodePeekIndex = -1; // read from start
 
                        line = 1;
                        column = 1;
@@ -1005,12 +1029,10 @@ namespace System.Xml
                        switch (fragType) {
                        case XmlNodeType.Attribute:
                                reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", "&quot;"));
-                               SkipTextDeclaration ();
                                break;
                        case XmlNodeType.Element:
                                currentState = XmlNodeType.Element;
                                allowMultipleRoot = true;
-                               SkipTextDeclaration ();
                                break;
                        case XmlNodeType.Document:
                                break;
@@ -1020,8 +1042,8 @@ namespace System.Xml
                }
 
 #if NET_2_0
-               [MonoTODO ("Test")]
                internal ConformanceLevel Conformance {
+                       get { return allowMultipleRoot ? ConformanceLevel.Fragment : ConformanceLevel.Document; }
                        set {
                                if (value == ConformanceLevel.Fragment) {
                                        currentState = XmlNodeType.Element;
@@ -1087,34 +1109,23 @@ namespace System.Xml
 
                private void ClearAttributes ()
                {
-                       for (int i = 0; i < attributeCount; i++)
-                               attributeTokens [i].Clear ();
+                       //for (int i = 0; i < attributeCount; i++)
+                       //      attributeTokens [i].Clear ();
                        attributeCount = 0;
                        currentAttribute = -1;
                        currentAttributeValue = -1;
                }
 
-               private int PeekChar ()
+               private int PeekSurrogate (int c)
                {
-                       if (peekCharsLength == peekCharsIndex) {
-                               if (!ReadTextReader (-1))
-                                       return -1;
-                               return PeekChar ();
-                       }
-
-                       char c = peekChars [peekCharsIndex];
-                       if (c == 0)
-                               return -1;
-                       if (!char.IsSurrogate (c))
-                               return c;
-                       if (peekCharsLength == peekCharsIndex + 1) {
+                       if (peekCharsLength <= peekCharsIndex + 1) {
                                if (!ReadTextReader (c))
                                        //FIXME: copy MS.NET behaviour when unpaired surrogate found
                                        return c;
                        }
 
-                       char highhalfChar = peekChars [peekCharsIndex];
-                       char lowhalfChar = peekChars [peekCharsIndex+1];
+                       int highhalfChar = peekChars [peekCharsIndex];
+                       int lowhalfChar = peekChars [peekCharsIndex+1];
 
                        if (((highhalfChar & 0xFC00) != 0xD800) || ((lowhalfChar & 0xFC00) != 0xDC00))
                                //FIXME: copy MS.NET behaviour when unpaired surrogate found
@@ -1122,10 +1133,25 @@ namespace System.Xml
                        return 0x10000 + (highhalfChar-0xD800)*0x400 + (lowhalfChar-0xDC00);
                }
 
+               private int PeekChar ()
+               {
+                       if (peekCharsIndex < peekCharsLength) {
+                               int c = peekChars [peekCharsIndex];
+                               if (c == 0)
+                                       return -1;
+                               if (c < 0xD800 || c >= 0xDFFF)
+                                       return c;
+                               return PeekSurrogate (c);
+                       } else {
+                               if (!ReadTextReader (-1))
+                                       return -1;
+                               return PeekChar ();
+                       }
+               }
+
                private int ReadChar ()
                {
                        int ch = PeekChar ();
-
                        peekCharsIndex++;
 
                        if (ch >= 0x10000)
@@ -1134,30 +1160,74 @@ namespace System.Xml
                        if (ch == '\n') {
                                line++;
                                column = 1;
-                       } else if (ch == -1) {
-                               return -1;
-                       } else {
+                       } else if (ch != -1) {
                                column++;
                        }
-                       if (currentState != XmlNodeType.Element)
-                               AppendCurrentTagChar (ch);
                        return ch;
                }
 
+               private void Advance (int ch) {
+                       peekCharsIndex++;
+
+                       if (ch >= 0x10000)
+                               peekCharsIndex++; //Increment by 2 when a compound UCS-4 character was found
+
+                       if (ch == '\n') {
+                               line++;
+                               column = 1;
+                       } else if (ch != -1) {
+                               column++;
+                       }
+               }
+
                private bool ReadTextReader (int remained)
                {
-                       peekCharsIndex = 0;
-                       if (remained >= 0)
-                               peekChars [0] = (char) remained;
+                       if (peekCharsLength < 0) {      // initialized buffer
+                               peekCharsLength = reader.Read (peekChars, 0, peekChars.Length);
+                               return peekCharsLength > 0;
+                       }
                        int offset = remained >= 0 ? 1 : 0;
-                       peekCharsLength = reader.Read (peekChars, offset,
-                               peekCharCapacity - offset) + offset;
-                       return (peekCharsLength != 0);
+                       int copysize = peekCharsLength - curNodePeekIndex;
+
+                       // It must assure that current tag content always exists
+                       // in peekChars.
+                       if (!preserveCurrentTag) {
+                               curNodePeekIndex = 0;
+                               peekCharsIndex = 0;
+                               //copysize = 0;
+                       } else if (peekCharsLength < peekChars.Length) {
+                               // NonBlockingStreamReader returned less bytes
+                               // than the size of the buffer. In that case,
+                               // just refill the buffer.
+                       } else if (curNodePeekIndex <= (peekCharsLength >> 1)) {
+                               // extend the buffer
+                               char [] tmp = new char [peekChars.Length * 2];
+                               Array.Copy (peekChars, curNodePeekIndex,
+                                       tmp, 0, copysize);
+                               peekChars = tmp;
+                               curNodePeekIndex = 0;
+                               peekCharsIndex = copysize;
+                       } else {
+                               Array.Copy (peekChars, curNodePeekIndex,
+                                       peekChars, 0, copysize);
+                               curNodePeekIndex = 0;
+                               peekCharsIndex = copysize;
+                       }
+                       if (remained >= 0)
+                               peekChars [peekCharsIndex] = (char) remained;
+                       int count = peekChars.Length - peekCharsIndex - offset;
+                       if (count > peekCharCapacity)
+                               count = peekCharCapacity;
+                       int read = reader.Read (
+                               peekChars, peekCharsIndex + offset, count);
+                       int remainingSize = offset + read;
+                       peekCharsLength = peekCharsIndex + remainingSize;
+
+                       return (remainingSize != 0);
                }
 
                private bool ReadContent ()
                {
-                       currentTagLength = 0;
                        if (popScope) {
                                parserContext.NamespaceManager.PopScope ();
                                parserContext.PopScope ();
@@ -1187,19 +1257,19 @@ namespace System.Xml
                                } else {
                                        switch (c) {
                                        case '<':
-                                               ReadChar ();
+                                               Advance (c);
                                                switch (PeekChar ())
                                                {
                                                case '/':
-                                                       ReadChar ();
+                                                       Advance ('/');
                                                        ReadEndTag ();
                                                        break;
                                                case '?':
-                                                       ReadChar ();
+                                                       Advance ('?');
                                                        ReadProcessingInstruction ();
                                                        break;
                                                case '!':
-                                                       ReadChar ();
+                                                       Advance ('!');
                                                        ReadDeclaration ();
                                                        break;
                                                default:
@@ -1211,13 +1281,9 @@ namespace System.Xml
                                        case '\n':
                                        case '\t':
                                        case ' ':
-                                               if (whitespaceHandling == WhitespaceHandling.All ||
-                                                       whitespaceHandling == WhitespaceHandling.Significant)
-                                                       ReadWhitespace ();
-                                               else {
-                                                       SkipWhitespace ();
+                                               if (!ReadWhitespace ())
+                                                       // skip
                                                        return ReadContent ();
-                                               }
                                                break;
                                        default:
                                                ReadText (true);
@@ -1300,13 +1366,13 @@ namespace System.Xml
                        }
 
                        if (PeekChar () == '/') {
-                               ReadChar ();
+                               Advance ('/');
                                isEmptyElement = true;
                                popScope = true;
                        }
                        else {
                                depthUp = true;
-                               PushElementName (name);
+                               PushElementName (name, localName, prefix);
                        }
                        parserContext.PushScope ();
 
@@ -1383,14 +1449,15 @@ namespace System.Xml
                                CheckCurrentStateUpdate ();
                }
 
-               private void PushElementName (string name)
+               private void PushElementName (string name, string local, string prefix)
                {
                        if (elementNames.Length == elementNameStackPos) {
-                               string [] newArray = new string [elementNames.Length * 2];
+                               TagName [] newArray = new TagName [elementNames.Length * 2];
                                Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
                                elementNames = newArray;
                        }
-                       elementNames [elementNameStackPos++] = name;
+                       elementNames [elementNameStackPos++] =
+                               new TagName (name, local, prefix);
                }
 
                // The reader is positioned on the first character
@@ -1403,13 +1470,10 @@ namespace System.Xml
                        currentLinkedNodeLineNumber = line;
                        currentLinkedNodeLinePosition = column;
 
-                       string prefix, localName;
-                       string name = ReadName (out prefix, out localName);
                        if (elementNameStackPos == 0)
                                throw NotWFError ("closing element without matching opening element");
-                       string expected = elementNames [--elementNameStackPos];
-                       if (expected != name)
-                               throw NotWFError (String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
+                       TagName expected = elementNames [--elementNameStackPos];
+                       Expect (expected.Name);
 
                        ExpectAfterWhitespace ('>');
 
@@ -1417,15 +1481,15 @@ namespace System.Xml
 
                        SetProperties (
                                XmlNodeType.EndElement, // nodeType
-                               name, // name
-                               prefix, // prefix
-                               localName, // localName
+                               expected.Name, // name
+                               expected.Prefix, // prefix
+                               expected.LocalName, // localName
                                false, // isEmptyElement
                                null, // value
                                true // clearAttributes
                        );
-                       if (prefix.Length > 0)
-                               currentToken.NamespaceURI = LookupNamespace (prefix, true);
+                       if (expected.Prefix.Length > 0)
+                               currentToken.NamespaceURI = LookupNamespace (expected.Prefix, true);
                        else if (namespaces)
                                currentToken.NamespaceURI = parserContext.NamespaceManager.DefaultNamespace;
 
@@ -1440,6 +1504,7 @@ namespace System.Xml
                                currentState = XmlNodeType.EndElement;
                }
 
+#if USE_NAME_BUFFER
                private void AppendSurrogatePairNameChar (int ch)
                {
                        nameBuffer [nameLength++] = (char) ((ch - 0x10000) / 0x400 + 0xD800);
@@ -1455,6 +1520,7 @@ namespace System.Xml
                        nameBuffer = new char [nameCapacity];
                        Array.Copy (oldNameBuffer, nameBuffer, nameLength);
                }
+#endif
 
                private void AppendValueChar (int ch)
                {
@@ -1472,6 +1538,27 @@ namespace System.Xml
 
                private string CreateValueString ()
                {
+                       // Since whitespace strings are mostly identical
+                       // depending on the Depth, we make use of NameTable
+                       // to atomize whitespace strings.
+                       switch (NodeType) {
+                       case XmlNodeType.Whitespace:
+                       case XmlNodeType.SignificantWhitespace:
+                               int len = valueBuffer.Length;
+                               if (whitespaceCache == null)
+                                       whitespaceCache = new char [32];
+                               if (len >= whitespaceCache.Length)
+                                       break;
+                               if (whitespacePool == null)
+                                       whitespacePool = new NameTable ();
+#if NET_2_0
+                               valueBuffer.CopyTo (0, whitespaceCache, 0, len);
+#else
+                               for (int i = 0; i < len; i++)
+                                       whitespaceCache [i] = valueBuffer [i];
+#endif
+                               return whitespacePool.Add (whitespaceCache, 0, valueBuffer.Length);
+                       }
                        return (valueBuffer.Capacity < 100) ?
                                valueBuffer.ToString (0, valueBuffer.Length) :
                                valueBuffer.ToString ();
@@ -1482,34 +1569,13 @@ namespace System.Xml
                        valueBuffer.Length = 0;
                }
 
-               private void AppendCurrentTagChar (int ch)
-               {
-                       if (currentTagLength == currentTagCapacity)
-                               ExpandCurrentTagCapacity ();
-                       if (ch < Char.MaxValue)
-                               currentTagBuffer [currentTagLength++] = (char) ch;
-                       else {
-                               currentTagBuffer [currentTagLength++] = (char) ((ch - 0x10000) / 0x400 + 0xD800);
-                               if (currentTagLength == currentTagCapacity)
-                                       ExpandCurrentTagCapacity ();
-                               currentTagBuffer [currentTagLength++] = (char) ((ch - 0x10000) % 0x400 + 0xDC00);
-                       }
-               }
-
-               private void ExpandCurrentTagCapacity ()
-               {
-                       currentTagCapacity = currentTagCapacity * 2;
-                       char [] oldCurrentTagBuffer = currentTagBuffer;
-                       currentTagBuffer = new char [currentTagCapacity];
-                       Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
-               }
-
                // The reader is positioned on the first character
                // of the text.
                private void ReadText (bool notWhitespace)
                {
                        if (currentState != XmlNodeType.Element)
                                throw NotWFError ("Text node cannot appear in this state.");
+                       preserveCurrentTag = false;
 
                        if (notWhitespace)
                                ClearValueBuffer ();
@@ -1583,7 +1649,7 @@ namespace System.Xml
                private int ReadReference (bool ignoreEntityReferences)
                {
                        if (PeekChar () == '#') {
-                               ReadChar ();
+                               Advance ('#');
                                return ReadCharacterReference ();
                        } else
                                return ReadEntityReference (ignoreEntityReferences);
@@ -1592,12 +1658,13 @@ namespace System.Xml
                private int ReadCharacterReference ()
                {
                        int value = 0;
+                       int ch;
 
                        if (PeekChar () == 'x') {
-                               ReadChar ();
+                               Advance ('x');
 
-                               while (PeekChar () != ';' && PeekChar () != -1) {
-                                       int ch = ReadChar ();
+                               while ((ch = PeekChar ()) != ';' && ch != -1) {
+                                       Advance (ch);
 
                                        if (ch >= '0' && ch <= '9')
                                                value = (value << 4) + ch - '0';
@@ -1612,8 +1679,8 @@ namespace System.Xml
                                                                ch));
                                }
                        } else {
-                               while (PeekChar () != ';' && PeekChar () != -1) {
-                                       int ch = ReadChar ();
+                               while ((ch = PeekChar ()) != ';' && ch != -1) {
+                                       Advance (ch);
 
                                        if (ch >= '0' && ch <= '9')
                                                value = value * 10 + ch - '0';
@@ -1705,7 +1772,7 @@ namespace System.Xml
                        currentAttributeValue = -1;
                }
 
-               private void AddDtdAttribute (string name, string value)
+               private void AddAttributeWithValue (string name, string value)
                {
                        IncrementAttributeToken ();
                        XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
@@ -1714,7 +1781,6 @@ namespace System.Xml
                        ati.NamespaceURI = String.Empty;
                        IncrementAttributeValueToken ();
                        XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
-                       vti.Value = value;
                        SetTokenProperties (vti,
                                XmlNodeType.Text,
                                String.Empty,
@@ -1723,6 +1789,7 @@ namespace System.Xml
                                false,
                                value,
                                false);
+                       ati.Value = value;
                        attributeCount++;
                }
 
@@ -1821,7 +1888,7 @@ namespace System.Xml
                                        goto default;
                                case '&':
                                        if (PeekChar () == '#') {
-                                               ReadChar ();
+                                               Advance ('#');
                                                ch = ReadCharacterReference ();
                                                AppendValueChar (ch);
                                                break;
@@ -1904,26 +1971,21 @@ namespace System.Xml
                private void ReadProcessingInstruction ()
                {
                        string target = ReadName ();
-                       if (target == "xml") {
-                               ReadXmlDeclaration ();
-                               return;
-                       } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml")
+                       if (target != "xml" && target.ToLower (CultureInfo.InvariantCulture) == "xml")
                                throw NotWFError ("Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
 
-                       if (currentState == XmlNodeType.None)
-                               currentState = XmlNodeType.XmlDeclaration;
-
                        if (!SkipWhitespace ())
                                if (PeekChar () != '?')
                                        throw NotWFError ("Invalid processing instruction name was found.");
 
                        ClearValueBuffer ();
 
-                       while (PeekChar () != -1) {
-                               int ch = ReadChar ();
+                       int ch;
+                       while ((ch = PeekChar ()) != -1) {
+                               Advance (ch);
 
                                if (ch == '?' && PeekChar () == '>') {
-                                       ReadChar ();
+                                       Advance ('>');
                                        break;
                                }
 
@@ -1932,48 +1994,76 @@ namespace System.Xml
                                AppendValueChar (ch);
                        }
 
-                       SetProperties (
-                               XmlNodeType.ProcessingInstruction, // nodeType
-                               target, // name
-                               String.Empty, // prefix
-                               target, // localName
-                               false, // isEmptyElement
-                               null, // value: create only when required
-                               true // clearAttributes
-                       );
+                       if (Object.ReferenceEquals (target, XmlNamespaceManager.PrefixXml))
+                               VerifyXmlDeclaration ();
+                       else {
+                               if (currentState == XmlNodeType.None)
+                                       currentState = XmlNodeType.XmlDeclaration;
+
+                               SetProperties (
+                                       XmlNodeType.ProcessingInstruction, // nodeType
+                                       target, // name
+                                       String.Empty, // prefix
+                                       target, // localName
+                                       false, // isEmptyElement
+                                       null, // value: create only when required
+                                       true // clearAttributes
+                               );
+                       }
                }
 
-               // The reader is positioned after "<?xml "
-               private void ReadXmlDeclaration ()
+               void VerifyXmlDeclaration ()
                {
-                       if (currentState != XmlNodeType.None) {
+                       if (!allowMultipleRoot && currentState != XmlNodeType.None)
                                throw NotWFError ("XML declaration cannot appear in this state.");
-                       }
-                       currentState = XmlNodeType.XmlDeclaration;
-
-                       ClearAttributes ();
-
-                       ReadAttributes (true);  // They must have "version."
-                       string version = GetAttribute ("version");
 
-                       string message = null;
+                       currentState = XmlNodeType.XmlDeclaration;
 
-                       if (attributeTokens [0].Name != "version" || version != "1.0")
-                               message = "Version 1.0 declaration is required in XML Declaration.";
-                       else if (attributeCount > 1 &&
-                                       (attributeTokens [1].Name != "encoding" &&
-                                       attributeTokens [1].Name != "standalone"))
-                               message = "Invalid Xml Declaration markup was found.";
-                       else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
-                               message = "Invalid Xml Declaration markup was found.";
-                       string sa = GetAttribute ("standalone");
-                       if (sa != null && sa != "yes" && sa != "no")
-                               message = String.Format ("Only 'yes' or 'no' is allowed for standalone. Value was '{0}'", sa);
+                       string text = CreateValueString ();
 
-                       this.isStandalone = (sa == "yes");
+                       ClearAttributes ();
 
-                       if (message != null)
-                               throw NotWFError (message);
+                       int idx = 0;
+
+                       string encoding = null, standalone = null;
+                       string name, value;
+                       ParseAttributeFromString (text, ref idx, out name, out value);
+                       if (name != "version" || value != "1.0")
+                               throw NotWFError ("'version' is expected.");
+                       name = String.Empty;
+                       if (SkipWhitespaceInString (text, ref idx) && idx < text.Length)
+                               ParseAttributeFromString (text, ref idx, out name, out value);
+                       if (name == "encoding") {
+                               if (!XmlChar.IsValidIANAEncoding (value))
+                                       throw NotWFError ("'encoding' must be a valid IANA encoding name.");
+                               if (reader is XmlStreamReader)
+                                       parserContext.Encoding = ((XmlStreamReader) reader).Encoding;
+                               else
+                                       parserContext.Encoding = Encoding.Unicode;
+                               encoding = value;
+                               name = String.Empty;
+                               if (SkipWhitespaceInString (text, ref idx) && idx < text.Length)
+                                       ParseAttributeFromString (text, ref idx, out name, out value);
+                       }
+                       if (name == "standalone") {
+                               this.isStandalone = value == "yes";
+                               if (value != "yes" && value != "no")
+                                       throw NotWFError ("Only 'yes' or 'no' is allow for 'standalone'");
+                               standalone = value;
+                               SkipWhitespaceInString (text, ref idx);
+                       }
+                       else if (name.Length != 0)
+                               throw NotWFError (String.Format ("Unexpected token: '{0}'", name));
+
+                       if (idx < text.Length)
+                               throw NotWFError ("'?' is expected.");
+
+                       AddAttributeWithValue ("version", "1.0");
+                       if (encoding != null)
+                               AddAttributeWithValue ("encoding", encoding);
+                       if (standalone != null)
+                               AddAttributeWithValue ("standalone", standalone);
+                       currentAttribute = currentAttributeValue = -1;
 
                        SetProperties (
                                XmlNodeType.XmlDeclaration, // nodeType
@@ -1981,17 +2071,55 @@ namespace System.Xml
                                String.Empty, // prefix
                                "xml", // localName
                                false, // isEmptyElement
-                               new string (currentTagBuffer, 6, currentTagLength - 6), // value
+                               text, // value
                                false // clearAttributes
                        );
+               }
 
-                       Expect ("?>");
+               bool SkipWhitespaceInString (string text, ref int idx)
+               {
+                       int start = idx;
+                       while (idx < text.Length && XmlChar.IsWhitespace (text [idx]))
+                               idx++;
+                       return idx - start > 0;
                }
 
-               private void SkipTextDeclaration ()
+               private void ParseAttributeFromString (string src,
+                       ref int idx, out string name, out string value)
                {
-                       this.currentState = XmlNodeType.Element;
+                       while (idx < src.Length && XmlChar.IsWhitespace (src [idx]))
+                               idx++;
+
+                       int start = idx;
+                       while (idx < src.Length && XmlChar.IsNameChar (src [idx]))
+                               idx++;
+                       name = src.Substring (start, idx - start);
+
+                       while (idx < src.Length && XmlChar.IsWhitespace (src [idx]))
+                               idx++;
+                       if (idx == src.Length || src [idx] != '=')
+                               throw NotWFError (String.Format ("'=' is expected after {0}", name));
+                       idx++;
+
+                       while (idx < src.Length && XmlChar.IsWhitespace (src [idx]))
+                               idx++;
+
+                       if (idx == src.Length || src [idx] != '"' && src [idx] != '\'')
+                               throw NotWFError ("'\"' or '\'' is expected.");
+
+                       char quote = src [idx];
+                       idx++;
+                       start = idx;
+
+                       while (idx < src.Length && src [idx] != quote)
+                               idx++;
+                       idx++;
 
+                       value = src.Substring (start, idx - start - 1);
+               }
+
+               internal void SkipTextDeclaration ()
+               {
                        if (PeekChar () != '<')
                                return;
 
@@ -2069,10 +2197,17 @@ namespace System.Xml
                                }
                                // Encoding value should be checked inside XmlInputStream.
                        }
-                       else
+#if NET_2_0
+                       // this condition is to check if this instance is
+                       // not created by XmlReader.Create() (which just
+                       // omits strict text declaration check).
+                       else if (Conformance == ConformanceLevel.Auto)
                                throw NotWFError ("Encoding declaration is mandatory in text declaration.");
+#endif
 
                        Expect ("?>");
+
+                       curNodePeekIndex = peekCharsIndex; // without this it causes incorrect value start indication.
                }
 
                // The reader is positioned on the first character after
@@ -2108,18 +2243,21 @@ namespace System.Xml
                        if (currentState == XmlNodeType.None)
                                currentState = XmlNodeType.XmlDeclaration;
 
+                       preserveCurrentTag = false;
+
                        ClearValueBuffer ();
 
-                       while (PeekChar () != -1) {
-                               int ch = ReadChar ();
+                       int ch;
+                       while ((ch = PeekChar ()) != -1) {
+                               Advance (ch);
 
                                if (ch == '-' && PeekChar () == '-') {
-                                       ReadChar ();
+                                       Advance ('-');
 
                                        if (PeekChar () != '>')
                                                throw NotWFError ("comments cannot contain '--'");
 
-                                       ReadChar ();
+                                       Advance ('>');
                                        break;
                                }
 
@@ -2146,6 +2284,7 @@ namespace System.Xml
                {
                        if (currentState != XmlNodeType.Element)
                                throw NotWFError ("CDATA section cannot appear in this state.");
+                       preserveCurrentTag = false;
 
                        ClearValueBuffer ();
 
@@ -2242,10 +2381,9 @@ namespace System.Xml
                                ReadChar ();
                                intSubsetStartLine = this.LineNumber;
                                intSubsetStartColumn = this.LinePosition;
-                               int startPos = currentTagLength;
+                               ClearValueBuffer ();
                                ReadInternalSubset ();
-                               int endPos = currentTagLength - 1;
-                               parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
+                               parserContext.InternalSubset = CreateValueString ();
                        }
                        // end of DOCTYPE decl.
                        ExpectAfterWhitespace ('>');
@@ -2266,9 +2404,9 @@ namespace System.Xml
                                );
 
                        if (publicId != null)
-                               AddDtdAttribute ("PUBLIC", publicId);
+                               AddAttributeWithValue ("PUBLIC", publicId);
                        if (systemId != null)
-                               AddDtdAttribute ("SYSTEM", systemId);
+                               AddAttributeWithValue ("SYSTEM", systemId);
                        currentAttribute = currentAttributeValue = -1;
                }
 
@@ -2341,16 +2479,31 @@ namespace System.Xml
                        get { return stateStack.Peek (); }
                }
 
+               private int ReadValueChar ()
+               {
+                       int ret = ReadChar ();
+                       AppendValueChar (ret);
+                       return ret;
+               }
+
+               private void ExpectAndAppend (string s)
+               {
+                       Expect (s);
+                       valueBuffer.Append (s);
+               }
+
                // Simply read but not generate any result.
                private void ReadInternalSubset ()
                {
                        bool continueParse = true;
 
                        while (continueParse) {
-                               switch (ReadChar ()) {
+                               switch (ReadValueChar ()) {
                                case ']':
                                        switch (State) {
                                        case DtdInputState.Free:
+                                               // chop extra ']'
+                                               valueBuffer.Remove (valueBuffer.Length - 1, 1);
                                                continueParse = false;
                                                break;
                                        case DtdInputState.InsideDoubleQuoted:
@@ -2370,21 +2523,21 @@ namespace System.Xml
                                        case DtdInputState.Comment:
                                                continue;       // well-formed
                                        }
-                                       int c = ReadChar ();
+                                       int c = ReadValueChar ();
                                        switch (c) {
                                        case '?':
                                                stateStack.Push (DtdInputState.PI);
                                                break;
                                        case '!':
-                                               switch (ReadChar ()) {
+                                               switch (ReadValueChar ()) {
                                                case 'E':
-                                                       switch (ReadChar ()) {
+                                                       switch (ReadValueChar ()) {
                                                        case 'L':
-                                                               Expect ("EMENT");
+                                                               ExpectAndAppend ("EMENT");
                                                                stateStack.Push (DtdInputState.ElementDecl);
                                                                break;
                                                        case 'N':
-                                                               Expect ("TITY");
+                                                               ExpectAndAppend ("TITY");
                                                                stateStack.Push (DtdInputState.EntityDecl);
                                                                break;
                                                        default:
@@ -2392,15 +2545,15 @@ namespace System.Xml
                                                        }
                                                        break;
                                                case 'A':
-                                                       Expect ("TTLIST");
+                                                       ExpectAndAppend ("TTLIST");
                                                        stateStack.Push (DtdInputState.AttlistDecl);
                                                        break;
                                                case 'N':
-                                                       Expect ("OTATION");
+                                                       ExpectAndAppend ("OTATION");
                                                        stateStack.Push (DtdInputState.NotationDecl);
                                                        break;
                                                case '-':
-                                                       Expect ('-');
+                                                       ExpectAndAppend ("-");
                                                        stateStack.Push (DtdInputState.Comment);
                                                        break;
                                                }
@@ -2442,15 +2595,15 @@ namespace System.Xml
                                        break;
                                case '?':
                                        if (State == DtdInputState.PI) {
-                                               if (ReadChar () == '>')
+                                               if (ReadValueChar () == '>')
                                                        stateStack.Pop ();
                                        }
                                        break;
                                case '-':
                                        if (State == DtdInputState.Comment) {
                                                if (PeekChar () == '-') {
-                                                       ReadChar ();
-                                                       Expect ('>');
+                                                       ReadValueChar ();
+                                                       ExpectAndAppend (">");
                                                        stateStack.Pop ();
                                                }
                                        }
@@ -2516,19 +2669,54 @@ namespace System.Xml
 
                private string ReadName (out string prefix, out string localName)
                {
-                       // FIXME: need to reject non-QName names?
+#if !USE_NAME_BUFFER
+                       bool savePreserve = preserveCurrentTag;
+                       preserveCurrentTag = true;
+
+                       int startOffset = peekCharsIndex - curNodePeekIndex;
+                       int ch = PeekChar ();
+                       if (!XmlChar.IsFirstNameChar (ch))
+                               throw NotWFError (String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
+                       Advance (ch);
+                       int length = 1;
+                       int colonAt = -1;
+
+                       while (XmlChar.IsNameChar ((ch = PeekChar ()))) {
+                               Advance (ch);
+                               if (ch == ':' && namespaces && colonAt < 0)
+                                       colonAt = length;
+                               length++;
+                       }
 
+                       int start = curNodePeekIndex + startOffset;
+
+                       string name = parserContext.NameTable.Add (
+                               peekChars, start, length);
+
+                       if (colonAt > 0) {
+                               prefix = parserContext.NameTable.Add (
+                                       peekChars, start, colonAt);
+                               localName = parserContext.NameTable.Add (
+                                       peekChars, start + colonAt + 1, length - colonAt - 1);
+                       } else {
+                               prefix = String.Empty;
+                               localName = name;
+                       }
+
+                       preserveCurrentTag = savePreserve;
+
+                       return name;
+#else
                        int ch = PeekChar ();
                        if (!XmlChar.IsFirstNameChar (ch))
                                throw NotWFError (String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
 
                        nameLength = 0;
 
-                       ch = ReadChar ();
+                       Advance (ch);
                        // AppendNameChar (ch);
                        {
-                               if (nameLength == nameCapacity)
-                                       ExpandNameCapacity ();
+                               // nameBuffer.Length is always non-0 so no need to ExpandNameCapacity () here
                                if (ch < Char.MaxValue)
                                        nameBuffer [nameLength++] = (char) ch;
                                else
@@ -2537,10 +2725,10 @@ namespace System.Xml
 
                        int colonAt = -1;
 
-                       while (XmlChar.IsNameChar (PeekChar ())) {
-                               ch = ReadChar ();
+                       while (XmlChar.IsNameChar ((ch = PeekChar ()))) {
+                               Advance (ch);
 
-                               if (namespaces && colonAt < 0 && ch == ':')
+                               if (ch == ':' && namespaces && colonAt < 0)
                                        colonAt = nameLength;
                                // AppendNameChar (ch);
                                {
@@ -2555,16 +2743,16 @@ namespace System.Xml
 
                        string name = parserContext.NameTable.Add (nameBuffer, 0, nameLength);
 
-                       if (namespaces && colonAt > 0) {
+                       if (colonAt > 0) {
                                prefix = parserContext.NameTable.Add (nameBuffer, 0, colonAt);
                                localName = parserContext.NameTable.Add (nameBuffer, colonAt + 1, nameLength - colonAt - 1);
-                       }
-                       else {
+                       } else {
                                prefix = String.Empty;
                                localName = name;
                        }
 
                        return name;
+#endif
                }
 
                // Read the next character and compare it against the
@@ -2585,9 +2773,10 @@ namespace System.Xml
 
                private void Expect (string expected)
                {
-                       int len = expected.Length;
-                       for(int i=0; i< len; i++)
-                               Expect (expected[i]);
+                       for (int i = 0; i < expected.Length; i++)
+                               if (ReadChar () != expected [i])
+                                       throw NotWFError (String.Format (CultureInfo.InvariantCulture, 
+                                               "'{0}' is expected", expected));
                }
 
                private void ExpectAfterWhitespace (char c)
@@ -2611,40 +2800,45 @@ namespace System.Xml
                        bool skipped = (ch == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD);
                        if (!skipped)
                                return false;
-                       ReadChar ();
+                       Advance (ch);
                        // FIXME: It should be inlined by the JIT.
 //                     while (XmlChar.IsWhitespace (PeekChar ()))
 //                             ReadChar ();
                        while ((ch = PeekChar ()) == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD)
-                               ReadChar ();
+                               Advance (ch);
                        return skipped;
                }
 
-               private void ReadWhitespace ()
+               private bool ReadWhitespace ()
                {
                        if (currentState == XmlNodeType.None)
                                currentState = XmlNodeType.XmlDeclaration;
 
-                       ClearValueBuffer ();
+                       bool savePreserve = preserveCurrentTag;
+                       preserveCurrentTag = true;
+                       int startOffset = peekCharsIndex - curNodePeekIndex; // it should be 0 for now though.
+
                        int ch = PeekChar ();
                        do {
-                               // FIXME: it might be optimized by the JIT later,
-//                             AppendValueChar (ReadChar ());
-                               {
-                                       ch = ReadChar ();
-                                       if (ch < Char.MaxValue)
-                                               valueBuffer.Append ((char) ch);
-                                       else
-                                               AppendSurrogatePairValueChar (ch);
-                               }
+                               Advance (ch);
+                               ch = PeekChar ();
                        // FIXME: It should be inlined by the JIT.
 //                     } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
-                               ch = PeekChar ();
                        } while (ch == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD);
 
-                       if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
+                       bool isText = currentState == XmlNodeType.Element && ch != -1 && ch != '<';
+
+                       if (!isText && (whitespaceHandling == WhitespaceHandling.None ||
+                                   whitespaceHandling == WhitespaceHandling.Significant && XmlSpace != XmlSpace.Preserve))
+                               return false;
+
+                       ClearValueBuffer ();
+                       valueBuffer.Append (peekChars, curNodePeekIndex, peekCharsIndex - curNodePeekIndex - startOffset);
+                       preserveCurrentTag = savePreserve;
+
+                       if (isText) {
                                ReadText (false);
-                       else {
+                       else {
                                XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
                                        XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
                                SetProperties (nodeType,
@@ -2656,7 +2850,7 @@ namespace System.Xml
                                               true);
                        }
 
-                       return;
+                       return true;
                }
 
                // Returns -1 if it should throw an error.
@@ -2671,7 +2865,7 @@ namespace System.Xml
                                case -1:
                                        throw NotWFError ("Unexpected end of xml.");
                                case '<':
-                                       ReadChar ();
+                                       Advance (c);
                                        if (PeekChar () != '/') {
                                                buffer [bufIndex++] = '<';
                                                continue;
@@ -2687,7 +2881,7 @@ namespace System.Xml
                                        Read (); // move to the next node
                                        return i;
                                default:
-                                       ReadChar ();
+                                       Advance (c);
                                        if (c < Char.MaxValue)
                                                buffer [bufIndex++] = (char) c;
                                        else {
@@ -2715,11 +2909,10 @@ namespace System.Xml
                                                continue;
                                        ReadChar ();
                                        string name = ReadName ();
-                                       if (name != elementNames [elementNameStackPos - 1])
+                                       if (name != elementNames [elementNameStackPos - 1].Name)
                                                continue;
                                        Expect ('>');
                                        depth--;
-                                       elementNames [--elementNameStackPos] = null;
                                        return Read ();
                                }
                        } while (true);