2003-02-03 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
index d18229315a0040c63e6c0633f417b98fe05caa2d..21162130b1893dbfb48b3ebc95d8cfb421fac7c4 100644 (file)
@@ -47,108 +47,80 @@ namespace System.Xml
                {
                }
 
-               [MonoTODO]
                public XmlTextReader (Stream input)
+                       : this (new StreamReader (input))
                {
-                       // We can share some code in the constructors (at least for this one and next 2)
-                       XmlNameTable nt = new NameTable ();
-                       XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
-                       parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
-                       Init ();
-                       reader = new StreamReader (input);
                }
 
-               [MonoTODO]
                public XmlTextReader (string url)
+                       : this(url, new NameTable ())
                {
-                       XmlNameTable nt = new NameTable ();
-                       XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
-                       parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
-                       Init ();
-                       // StreamReader does not support url, only filepath;-)
-                       reader = new StreamReader(url);
                }
 
-               [MonoTODO]
                public XmlTextReader (TextReader input)
+                       : this (input, new NameTable ())
                {
-                       XmlNameTable nt = new NameTable ();
-                       XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
-                       parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
-                       Init ();
-                       reader = input;
                }
 
-               [MonoTODO]
                protected XmlTextReader (XmlNameTable nt)
+                       : this (String.Empty, null, XmlNodeType.None, null)
                {
-                       throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public XmlTextReader (Stream input, XmlNameTable nt)
+                       : this(new StreamReader (input), nt)
                {
-                       XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
-                       parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
-                       Init ();
-                       reader = new StreamReader (input);
                }
 
-               [MonoTODO]
                public XmlTextReader (string url, Stream input)
+                       : this (url, new StreamReader (input))
                {
-                       throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public XmlTextReader (string url, TextReader input)
+                       : this (url, input, new NameTable ())
                {
-                       throw new NotImplementedException ();
                }
 
-               [MonoTODO]
+               [MonoTODO("Non-filename-url must be supported. Waiting for WebClient")]
                public XmlTextReader (string url, XmlNameTable nt)
+                       // : this(url, new StreamReader ((Stream)new XmlUrlResolver ().GetEntity (new Uri (url), null, typeof(Stream))), nt)
+                       : this (url, new StreamReader (url), nt)
                {
-                       throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public XmlTextReader (TextReader input, XmlNameTable nt)
+                       : this(String.Empty, input, nt)
                {
-                       XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
-                       parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
-                       Init ();
-                       reader = input;
                }
 
-               [MonoTODO]
                public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
+                       : this (String.Empty, new StreamReader (xmlFragment), fragType, context)
                {
-                       parserContext = context;
-                       reader = new StreamReader(xmlFragment);
-                       Init();
-//                     throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public XmlTextReader (string url, Stream input, XmlNameTable nt)
+                       : this (url, new StreamReader (input), nt)
                {
-                       throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public XmlTextReader (string url, TextReader input, XmlNameTable nt)
+                       : this (url, input, XmlNodeType.Document, new XmlParserContext (nt, new XmlNamespaceManager (nt), null, XmlSpace.None))
                {
-                       throw new NotImplementedException ();
                }
 
-               [MonoTODO]
+               [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
                public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
+                       : this (String.Empty, new StringReader (xmlFragment), fragType, context)
                {
-                       //Waiting for Validating reader for fragType rules.
-                       parserContext = context;
-                       Init ();
-                       reader = new StringReader(xmlFragment);
+               }
+
+               // TODO still remains as described at head of this file,
+               // but it might not be TODO of the constructors...
+               XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
+               {
+                       this.SetReaderContext(url, context);
+                       this.SetReaderFragment(fragment, fragType);
                }
 
                #endregion
@@ -275,10 +247,15 @@ namespace System.Xml
                        get { return prefix; }
                }
 
-               [MonoTODO]
                public override char QuoteChar
                {
-                       get { throw new NotImplementedException (); }
+                       get {
+                               // value string holds attribute quotation char.
+                               if (NodeType == XmlNodeType.Attribute)
+                                       return value [0];
+                               else
+                                       return '"';
+                       }
                }
 
                public override ReadState ReadState
@@ -288,7 +265,12 @@ namespace System.Xml
 
                public override string Value
                {
-                       get { return value; }
+                       get {
+                               if(NodeType == XmlNodeType.Attribute)
+                                       return ResolveAttributeValue(value);
+                               else
+                                       return value;
+                       }
                }
 
                public WhitespaceHandling WhitespaceHandling
@@ -331,13 +313,13 @@ namespace System.Xml
                        if (i > attributes.Count)
                                throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
                        else
-                               throw new NotImplementedException ();
+                               return ResolveAttributeValue (attributes [orderedAttributes [i]] as string);
                }
 
                public override string GetAttribute (string name)
                {
                        return attributes.ContainsKey (name) ?
-                               attributes [name] as string : String.Empty;
+                               ResolveAttributeValue (attributes [name] as string) : String.Empty;
                }
 
                public override string GetAttribute (string localName, string namespaceURI)
@@ -357,11 +339,11 @@ namespace System.Xml
 
                                                if (namespaceURI == thisNamespaceURI)
                                                        return attributes.ContainsKey (thisName) ?
-                                                               attributes [thisName] as string : String.Empty;
+                                                               ResolveAttributeValue (attributes [thisName] as string) : String.Empty;
                                        }
                                } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
                                        return attributes.ContainsKey (thisName) ? 
-                                               attributes [thisName] as string : String.Empty;
+                                               ResolveAttributeValue (attributes [thisName] as string) : String.Empty;
                        }
 
                        return String.Empty;
@@ -384,10 +366,22 @@ namespace System.Xml
                        return parserContext.NamespaceManager.LookupNamespace (prefix);
                }
 
-               [MonoTODO]
                public override void MoveToAttribute (int i)
                {
-                       throw new NotImplementedException ();
+                       MoveToElement ();
+
+                       if (attributes == null || orderedAttributes.Count < i || i < 0)
+                               throw new ArgumentOutOfRangeException ("attribute index out of range.");
+
+                       string name = orderedAttributes [i] as string;
+                       string value = attributes [name] as string;
+                       SetProperties (
+                               XmlNodeType.Attribute, // nodeType
+                               name, // name
+                               false, // isEmptyElement
+                               value, // value
+                               false // clearAttributes
+                               );
                }
 
                public override bool MoveToAttribute (string name)
@@ -411,7 +405,6 @@ namespace System.Xml
                        }
 
                        if (match) {
-                                       
                                string value = attributes [name] as string;
                                SetProperties (
                                        XmlNodeType.Attribute, // nodeType
@@ -421,7 +414,7 @@ namespace System.Xml
                                        false // clearAttributes
                                );
                        }
-                       
+
                        return match;
                }
 
@@ -485,10 +478,82 @@ namespace System.Xml
                        return more;
                }
 
-               [MonoTODO]
+               [MonoTODO("This method should consider entity references")]
                public override bool ReadAttributeValue ()
                {
-                       throw new NotImplementedException ();
+                       // reading attribute value phase now stopped
+                       if(attributeStringCurrentPosition < 0 ||
+                                       attributeString.Length < attributeStringCurrentPosition) {
+                               attributeStringCurrentPosition = 0;
+                               attributeString = String.Empty;
+                               return false;
+                       }
+
+                       // If not started, then initialize attributeString when parsing is at start.
+                       if(attributeStringCurrentPosition == 0)
+                               attributeString =
+                                       value.Substring (1, value.Length - 2);
+
+                       bool returnEntity = false;
+                       value = String.Empty;
+                       int nextPosition = attributeString.IndexOf ('&',
+                               attributeStringCurrentPosition);
+
+                       // if attribute string starts from '&' then it may be (unparsable) entity reference.
+                       if(nextPosition == 0) {
+                               string parsed = ReadAttributeValueEntityReference ();
+                               if(parsed == null) {
+                                       // return entity (It is only this case to return entity reference.)
+                                       int endEntityPosition = attributeString.IndexOf (';',
+                                               attributeStringCurrentPosition);
+                                       SetProperties (XmlNodeType.EntityReference,
+                                               attributeString.Substring (attributeStringCurrentPosition + 1,
+                                               endEntityPosition - attributeStringCurrentPosition - 1),
+                                               false,
+                                               String.Empty,
+                                               false);
+                                       attributeStringCurrentPosition = endEntityPosition + 1;
+
+                                       return true;
+                               }
+                               else
+                                       value += parsed;
+                       }
+
+                       // Other case always set text node.
+                       while(!returnEntity) {
+                               nextPosition = attributeString.IndexOf ('&', attributeStringCurrentPosition);
+                               if(nextPosition < 0) {
+                                       // Reached to the end of value string.
+                                       value += attributeString.Substring (attributeStringCurrentPosition);
+                                       attributeStringCurrentPosition = -1;
+                                       break;
+                               } else if(nextPosition == attributeStringCurrentPosition) {
+                                       string parsed = ReadAttributeValueEntityReference ();
+                                       if(parsed != null)
+                                               value += parsed;
+                                       else {
+                                               // Found that an entity reference starts from this point.
+                                               // Then once stop to parse attribute value and then return text.
+                                               value += attributeString.Substring (attributeStringCurrentPosition,
+                                                       nextPosition - attributeStringCurrentPosition);
+                                               break;
+                                       }
+                               } else {
+                                       value += attributeString.Substring (attributeStringCurrentPosition,
+                                               nextPosition - attributeStringCurrentPosition);
+                                       attributeStringCurrentPosition = nextPosition;
+                                       continue;
+                               }
+                       }
+
+                       SetProperties(XmlNodeType.Text,
+                               "#text",
+                               false,
+                               value,
+                               false);
+
+                       return true;
                }
 
                [MonoTODO]
@@ -527,7 +592,7 @@ namespace System.Xml
                                        endname = this.Name;
                                }
 
-                               xmlBuffer.Replace(currentTag.ToString (), "");
+                               xmlBuffer.Replace (currentTag.ToString (), "");
                                saveToXmlBuffer = false;
                                string InnerXml = xmlBuffer.ToString ();
                                xmlBuffer.Length = 0;
@@ -539,18 +604,17 @@ namespace System.Xml
                public override string ReadOuterXml ()
                {
                        if (NodeType == XmlNodeType.Attribute) {
-                               return Name+"=\""+Value+"\"";
+                               return Name + "=\"" + Value.Replace ("\"", "&quot;") + "\"";
                        } else {
                                saveToXmlBuffer = true;
-                               xmlBuffer.Append(currentTag.ToString ());
-                               string startname = this.Name;
-                               string endname = string.Empty;
+                               xmlBuffer.Append (currentTag.ToString ());
+                               int startDepth = Depth;
                                readState = ReadState.Interactive;
 
-                               while (startname != endname) {
+                               do {
                                        ReadContent ();
-                                       endname = this.Name;
-                               }
+                               } while (Depth > startDepth);
+
                                saveToXmlBuffer = false;
                                string OuterXml = xmlBuffer.ToString ();
                                xmlBuffer.Length = 0;
@@ -573,12 +637,52 @@ namespace System.Xml
                public override void ResolveEntity ()
                {
                        // XmlTextReaders don't resolve entities.
-                       throw new InvalidOperationException ("XmlTextReaders don't resolve entities.");
+                       throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
                }
 
                #endregion
 
-               // privates
+               #region Internals
+               internal string publicId;
+               internal string systemId;
+
+               internal void SetReaderContext (string url, XmlParserContext context)
+               {
+                       parserContext = context;
+                       parserContext.BaseURI = url;
+                       Init ();
+               }
+
+               internal void SetReaderFragment(TextReader fragment, XmlNodeType fragType)
+               {
+                       this.reader = fragment;
+                       can_seek = fragment != null && fragment.Peek () != -1;
+
+                       if (fragType == XmlNodeType.Attribute)
+                               value = "''";
+/*     for future use
+                       switch(fragType)
+                       {
+                       case XmlNodeType.Attribute:     // attribute content
+                               parserContext.InputState = XmlParserInputState.AttributeValue;
+                               break;
+                       case XmlNodeType.DocumentFragment:      // element content
+                               parserContext.InputState = XmlParserInputState.Content;
+                               break;
+                       case XmlNodeType.Element:       // one element
+                               parserContext.InputState = XmlParserInputState.StartTag;
+                               break;
+                       case XmlNodeType.Document:      // document content
+                               parserContext.InputState = XmlParserInputState.Start;
+                               break;
+                       default:
+                               throw new InvalidOperationException("setting this xml node type not allowed.");
+                       }
+*/
+               }
+               #endregion
+
+               #region Privates
 
                private XmlParserContext parserContext;
 
@@ -628,6 +732,12 @@ namespace System.Xml
                private bool saveToXmlBuffer;
                private int line = 1;
                private int column = 1;
+               private bool has_peek;
+               private bool can_seek;
+               private int peek_char;
+
+               private string attributeString = String.Empty;
+               private int attributeStringCurrentPosition;
 
                private void Init ()
                {
@@ -738,12 +848,27 @@ namespace System.Xml
 
                private int PeekChar ()
                {
-                       return reader.Peek ();
+                       if (can_seek)
+                               return reader.Peek ();
+
+                       if (has_peek)
+                               return peek_char;
+
+                       peek_char = reader.Read ();
+                       has_peek = true;
+                       return peek_char;
                }
 
                private int ReadChar ()
                {
-                       int ch = reader.Read ();
+                       int ch;
+                       if (has_peek) {
+                               ch = peek_char;
+                               has_peek = false;
+                       } else {
+                               ch = reader.Read ();
+                       }
+
                        if (ch == '\n') {
                                line++;
                                column = 1;
@@ -1109,9 +1234,9 @@ namespace System.Xml
                                SkipWhitespace ();
 
                                if (name == "xmlns")
-                                       parserContext.NamespaceManager.AddNamespace (String.Empty, value);
+                                       parserContext.NamespaceManager.AddNamespace (String.Empty, ResolveAttributeValue (value));
                                else if (name.StartsWith ("xmlns:"))
-                                       parserContext.NamespaceManager.AddNamespace (name.Substring (6), value);
+                                       parserContext.NamespaceManager.AddNamespace (name.Substring (6), ResolveAttributeValue (value));
 
                                AddAttribute (name, value);
                        } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
@@ -1120,12 +1245,15 @@ namespace System.Xml
                // The reader is positioned on the quote character.
                private string ReadAttribute ()
                {
+                       valueLength = 0;
+
                        int quoteChar = ReadChar ();
 
                        if (quoteChar != '\'' && quoteChar != '\"')
                                throw new XmlException ("an attribute value was not quoted");
 
-                       valueLength = 0;
+                       // this keeps quote char to get QuoteChar property correctly.
+                       AppendValueChar (quoteChar);
 
                        while (PeekChar () != quoteChar) {
                                int ch = ReadChar ();
@@ -1134,9 +1262,10 @@ namespace System.Xml
                                {
                                case '<':
                                        throw new XmlException ("attribute values cannot contain '<'");
-                               case '&':
-                                       ReadReference (true);
-                                       break;
+// expansion of entity now should be done at ResolveAttributeValue() method
+//                             case '&':
+//                                     ReadReference (true);
+//                                     break;
                                case -1:
                                        throw new XmlException ("unexpected end of file in an attribute value");
                                default:
@@ -1146,6 +1275,7 @@ namespace System.Xml
                        }
 
                        ReadChar (); // quoteChar
+                       AppendValueChar (quoteChar);
 
                        return CreateValueString ();
                }
@@ -1172,10 +1302,17 @@ namespace System.Xml
                                AppendValueChar ((char)ch);
                        }
 
-                       SetProperties (\r
-                               target == "xml" ?\r
-                               XmlNodeType.XmlDeclaration :\r
-                               XmlNodeType.ProcessingInstruction, // nodeType\r
+/* for future use
+                       if(target == "xml") && parserContext.InputState != XmlParserInputState.Start)
+                               throw new XmlException("Xml declaration is not allowed here.");
+                       else {
+                               parserContext.InputState = XmlParserInputState.DTD; //for future use
+                       }
+*/
+                       SetProperties (
+                               target == "xml" ?
+                               XmlNodeType.XmlDeclaration :
+                               XmlNodeType.ProcessingInstruction, // nodeType
                                target, // name
                                false, // isEmptyElement
                                CreateValueString (), // value
@@ -1279,69 +1416,49 @@ namespace System.Xml
                        string doctypeName = null;
                        string publicId = String.Empty;
                        string systemId = String.Empty;
-                       string internalSubset = String.Empty;
 
-                       SkipWhitespace();
-                       doctypeName = ReadName();
-                       SkipWhitespace();
+                       SkipWhitespace ();
+                       doctypeName = ReadName ();
+                       SkipWhitespace ();
                        xmlBuffer.Length = 0;
-                       switch(PeekChar())
+                       switch(PeekChar ())
                        {
                        case 'S':
-                               systemId = ReadSystemLiteral();
+                               systemId = ReadSystemLiteral (true);
                                break;
                        case 'P':
-/*
-                               Expect("PUBLIC");
-                               SkipWhitespace();
-                               int quoteChar = ReadChar();
-                               int c = 0;
-                               while(c != quoteChar)
-                               {
-                                       c = ReadChar();
-                                       if(c < 0)
-                                               throw new XmlException("Unexpected end of stream in ExternalID.");
-                                       if(c != quoteChar)
-                                       {
-                                               if(XmlChar.IsPubidChar(c)) xmlBuffer.Append((char)c);
-                                               else
-                                                       throw new XmlException("character '" + (char)c + "' not allowed for PUBLIC ID");
-                                       }
-                               }
-                               publicId = xmlBuffer.ToString();
-                               */
-                               publicId = ReadPubidLiteral();
-                               SkipWhitespace();
-                               systemId = ReadSystemLiteral();
+                               publicId = ReadPubidLiteral ();
+                               SkipWhitespace ();
+                               systemId = ReadSystemLiteral (false);
                                break;
                        }
-                       SkipWhitespace();
+                       SkipWhitespace ();
 
 
-                       if(PeekChar() == '[')
+                       if(PeekChar () == '[')
                        {
                                // read markupdecl etc. or end of decl
-                               ReadChar();
+                               ReadChar ();
+                               xmlBuffer.Length = 0;
                                saveToXmlBuffer = true;
-                               do
-                               {
-                                       ReadDTDInternalSubset();
+                               do {
+                                       ReadDTDInternalSubset ();
                                } while(nodeType != XmlNodeType.None);
-                               xmlBuffer.Remove(xmlBuffer.Length -1, 1);       // cut off ']'
+                               xmlBuffer.Remove (xmlBuffer.Length - 1, 1);     // cut off ']'
                                saveToXmlBuffer = false;
                        }
                        // end of DOCTYPE decl.
-                       SkipWhitespace();
-                       Expect('>');
+                       SkipWhitespace ();
+                       Expect ('>');
 
-                       internalSubset = xmlBuffer.ToString();
+                       parserContext.InternalSubset = xmlBuffer.ToString ();
 
                        // set properties for <!DOCTYPE> node
                        SetProperties (
                                XmlNodeType.DocumentType, // nodeType
                                doctypeName, // name
                                false, // isEmptyElement
-                               internalSubset, // value
+                               parserContext.InternalSubset, // value
                                true // clearAttributes
                                );
                }
@@ -1354,124 +1471,125 @@ namespace System.Xml
                //       (if None then ']' was found.)
                private void ReadDTDInternalSubset()
                {
-                       SkipWhitespace();
-                       switch(ReadChar())
+                       SkipWhitespace ();
+                       switch(ReadChar ())
                        {
                        case ']':
                                nodeType = XmlNodeType.None;
                                break;
                        case '%':
-                               string peName = ReadName();
-                               Expect(';');
+                               string peName = ReadName ();
+                               Expect (';');
                                nodeType = XmlNodeType.EntityReference; // It's chating a bit;-)
                                break;
                        case '<':
-                               switch(ReadChar())
+                               switch(ReadChar ())
                                {
                                case '?':
-                                       ReadProcessingInstruction();
+                                       ReadProcessingInstruction ();
                                        break;
                                case '!':
-                                       switch(ReadChar())
+                                       switch(ReadChar ())
                                        {
                                        case '-':
-                                               Expect('-');
-                                               ReadComment();
+                                               Expect ('-');
+                                               ReadComment ();
                                                break;
                                        case 'E':
-                                               switch(ReadChar())
+                                               switch(ReadChar ())
                                                {
                                                case 'N':
-                                                       Expect("TITY");
-                                                       ReadEntityDecl();
+                                                       Expect ("TITY");
+                                                       ReadEntityDecl ();
                                                        break;
                                                case 'L':
-                                                       Expect("EMENT");
-                                                       ReadElementDecl();
+                                                       Expect ("EMENT");
+                                                       ReadElementDecl ();
                                                        break;
                                                default:
-                                                       throw new XmlException("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
+                                                       throw new XmlException ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
                                                }
                                                break;
                                        case 'A':
-                                               Expect("TTLIST");
-                                               ReadAttListDecl();
+                                               Expect ("TTLIST");
+                                               ReadAttListDecl ();
                                                break;
                                        case 'N':
-                                               Expect("OTATION");
-                                               ReadNotationDecl();
+                                               Expect ("OTATION");
+                                               ReadNotationDecl ();
                                                break;
                                        default:
-                                               throw new XmlException("Syntax Error after '<!' characters.");
+                                               throw new XmlException ("Syntax Error after '<!' characters.");
                                        }
                                        break;
                                default:
-                                       throw new XmlException("Syntax Error after '<' character.");
+                                       throw new XmlException ("Syntax Error after '<' character.");
                                }
                                break;
                        default:
-                               throw new XmlException("Syntax Error inside doctypedecl markup.");
+                               throw new XmlException ("Syntax Error inside doctypedecl markup.");
                        }
                }
 
                // The reader is positioned on the head of the name.
                private void ReadElementDecl()
                {
-                       while(ReadChar() != '>');
+                       while(ReadChar () != '>');
                }
 
                private void ReadEntityDecl()
                {
-                       while(ReadChar() != '>');
+                       while(ReadChar () != '>');
                }
 
                private void ReadAttListDecl()
                {
-                       while(ReadChar() != '>');
+                       while(ReadChar () != '>');
                }
 
                private void ReadNotationDecl()
                {
-                       while(ReadChar() != '>');
+                       while(ReadChar () != '>');
                }
                
                // The reader is positioned on the first 'S' of "SYSTEM".
-               private string ReadSystemLiteral ()
+               private string ReadSystemLiteral (bool expectSYSTEM)
                {
-                       Expect("SYSTEM");
-                       SkipWhitespace();
-                       int quoteChar = ReadChar();     // apos or quot
+                       if(expectSYSTEM)
+                               Expect ("SYSTEM");
+                       SkipWhitespace ();
+                       int quoteChar = ReadChar ();    // apos or quot
                        xmlBuffer.Length = 0;
                        saveToXmlBuffer = true;
                        int c = 0;
-                       while(c != quoteChar)
-                       {
-                               c = ReadChar();
-                               if(c < 0) throw new XmlException("Unexpected end of stream in ExternalID.");
+                       while(c != quoteChar) {
+                               c = ReadChar ();
+                               if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
                        }
                        saveToXmlBuffer = false;
-                       xmlBuffer.Remove(xmlBuffer.Length-1, 1);        // cut quoteChar
-                       return xmlBuffer.ToString();
+                       xmlBuffer.Remove (xmlBuffer.Length-1, 1);       // cut quoteChar
+                       return xmlBuffer.ToString ();
                }
 
                private string ReadPubidLiteral()
                {
-                       Expect("PUBLIC");
-                       SkipWhitespace();
-                       int quoteChar = ReadChar();
+                       Expect ("PUBLIC");
+                       SkipWhitespace ();
+                       int quoteChar = ReadChar ();
                        xmlBuffer.Length = 0;
                        saveToXmlBuffer = true;
                        int c = 0;
                        while(c != quoteChar)
                        {
-                               c = ReadChar();
-                               if(c < 0) throw new XmlException("Unexpected end of stream in ExternalID.");
-                               if(c != quoteChar && !XmlChar.IsPubidChar(c))
+                               c = ReadChar ();
+                               if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
+                               if(c != quoteChar && !XmlChar.IsPubidChar (c))
                                        throw new XmlException("character '" + (char)c + "' not allowed for PUBLIC ID");
                        }
-                       xmlBuffer.Remove(xmlBuffer.Length-1, 1);        // cut quoteChar
+                       ReadChar();     // skips quoteChar
+                       xmlBuffer.Remove (xmlBuffer.Length-1, 1);       // cut quoteChar
                        saveToXmlBuffer = false;
-                       return xmlBuffer.ToString();
+                       return xmlBuffer.ToString ();
                }
 
                // The reader is positioned on the first character
@@ -1513,7 +1631,7 @@ namespace System.Xml
                {
                        int len = expected.Length;
                        for(int i=0; i< len; i++)
-                               Expect(expected[i]);
+                               Expect (expected[i]);
                }
 
                // Does not consume the first non-whitespace character.
@@ -1543,5 +1661,97 @@ namespace System.Xml
 
                        return (PeekChar () != -1);
                }
+
+               // read entity reference from attribute string and if parsable then return the value.
+               private string ReadAttributeValueEntityReference ()
+               {
+                       int endEntityPosition = attributeString.IndexOf(';',
+                               attributeStringCurrentPosition);
+                       string entityName = attributeString.Substring (attributeStringCurrentPosition + 1,
+                               endEntityPosition - attributeStringCurrentPosition - 1);
+
+                       attributeStringCurrentPosition = endEntityPosition + 1;
+
+                       if(entityName [0] == '#') {
+                               char c;
+                               // character entity
+                               if(entityName [1] == 'x') {
+                                       // hexadecimal
+                                       c = (char) int.Parse ("0" + entityName.Substring (2),
+                                               System.Globalization.NumberStyles.HexNumber);
+                               } else {
+                                       // decimal
+                                       c = (char) int.Parse (entityName.Substring (1));
+                               }
+                               return c.ToString();
+                       }
+                       else {
+                               switch(entityName)
+                               {
+                               case "lt": return "<";
+                               case "gt": return ">";
+                               case "amp": return "&";
+                               case "quot": return "\"";
+                               case "apos": return "'";
+                               default: return null;
+                               }
+                       }
+               }
+
+               private string ResolveAttributeValue (string unresolved)
+               {
+                       if(unresolved == null) return null;
+                       StringBuilder resolved = new StringBuilder();
+                       int pos = 0;
+
+                       // trim start/end edge of quotation character.
+                       unresolved = unresolved.Substring (1, unresolved.Length - 2);
+
+                       int next = unresolved.IndexOf ('&');
+                       if(next < 0)
+                               return unresolved;
+
+                       while(next >= 0) {
+                               if(pos < next)
+                                       resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
+                               int endPos = unresolved.IndexOf (';', next+1);
+                               string entityName =
+                                       unresolved.Substring (next + 1, endPos - next - 1);
+                               if(entityName [0] == '#') {
+                                       char c;
+                                       // character entity
+                                       if(entityName [1] == 'x') {
+                                               // hexadecimal
+                                               c = (char) int.Parse ("0" + entityName.Substring (2),
+                                                       System.Globalization.NumberStyles.HexNumber);
+                                       } else {
+                                               // decimal
+                                               c = (char) int.Parse (entityName.Substring (1));
+                                       }
+                                       resolved.Append (c);
+                               } else {
+                                       switch(entityName) {
+                                       case "lt": resolved.Append ("<"); break;
+                                       case "gt": resolved.Append (">"); break;
+                                       case "amp": resolved.Append ("&"); break;
+                                       case "quot": resolved.Append ("\""); break;
+                                       case "apos": resolved.Append ("'"); break;
+                                       // With respect to "Value", MS document is helpless
+                                       // and the implemention returns inconsistent value
+                                       // (e.g. XML: "&ent; &amp;ent;" ---> Value: "&ent; &ent;".)
+                                       default: resolved.Append ("&" + entityName + ";"); break;
+                                       }
+                               }
+                               pos = endPos + 1;
+                               if(pos > unresolved.Length)
+                                       break;
+                               next = unresolved.IndexOf('&', pos);
+                       }
+                       resolved.Append (unresolved.Substring(pos));
+
+                       return resolved.ToString();
+               }
+
+               #endregion
        }
 }