X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.XML%2FSystem.Xml%2FXmlTextReader.cs;h=21162130b1893dbfb48b3ebc95d8cfb421fac7c4;hb=4477135bd8e477678d3ddaa4b508ff780b9e0a47;hp=cb2b68dc13cbfcfaab5eb4fc00c3ac5c36f26145;hpb=eb67401dcbc47e10421c610b306a0760a9bbe3ec;p=mono.git diff --git a/mcs/class/System.XML/System.Xml/XmlTextReader.cs b/mcs/class/System.XML/System.Xml/XmlTextReader.cs index cb2b68dc13c..21162130b18 100644 --- a/mcs/class/System.XML/System.Xml/XmlTextReader.cs +++ b/mcs/class/System.XML/System.Xml/XmlTextReader.cs @@ -3,6 +3,7 @@ // // Author: // Jason Diamond (jason@injektilo.org) +// Adam Treat (manyoso@yahoo.com) // // (C) 2001, 2002 Jason Diamond http://injektilo.org/ // @@ -12,6 +13,8 @@ // instructions, and comments are OK. // // It barfs on DOCTYPE declarations. +// => No barfing, but parsing is incomplete. +// DTD nodes are not still created. // // There's also no checking being done for either well-formedness // or validity. @@ -37,98 +40,87 @@ namespace System.Xml { public class XmlTextReader : XmlReader, IXmlLineInfo { + WhitespaceHandling whitespaceHandling = WhitespaceHandling.All; #region Constructors - [MonoTODO] protected XmlTextReader () { - throw new NotImplementedException (); } - [MonoTODO] public XmlTextReader (Stream input) + : this (new StreamReader (input)) { - throw new NotImplementedException (); } - [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 (); - 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) { - throw new NotImplementedException (); } - [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) { - throw new NotImplementedException (); } - [MonoTODO] public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context) + : this (String.Empty, new StreamReader (xmlFragment), fragType, context) { - 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) { - throw new NotImplementedException (); + } + + // 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 @@ -140,21 +132,21 @@ namespace System.Xml get { return attributes.Count; } } - [MonoTODO] public override string BaseURI { - get { throw new NotImplementedException (); } + get { return parserContext.BaseURI; } } public override int Depth { - get { return depth > 0 ? depth : 0; } + get { + return elementDepth; + } } - [MonoTODO] public Encoding Encoding { - get { throw new NotImplementedException (); } + get { return parserContext.Encoding; } } public override bool EOF @@ -201,16 +193,14 @@ namespace System.Xml get { return GetAttribute (localName, namespaceName); } } - [MonoTODO] public int LineNumber { - get { throw new NotImplementedException (); } + get { return line; } } - [MonoTODO] public int LinePosition { - get { throw new NotImplementedException (); } + get { return column; } } public override string LocalName @@ -257,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 @@ -270,14 +265,18 @@ namespace System.Xml public override string Value { - get { return value; } + get { + if(NodeType == XmlNodeType.Attribute) + return ResolveAttributeValue(value); + else + return value; + } } - [MonoTODO] public WhitespaceHandling WhitespaceHandling { - get { throw new NotImplementedException (); } - set { throw new NotImplementedException (); } + get { return whitespaceHandling; } + set { whitespaceHandling = value; } } [MonoTODO] @@ -311,12 +310,16 @@ namespace System.Xml [MonoTODO] public override string GetAttribute (int i) { - throw new NotImplementedException (); + if (i > attributes.Count) + throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount"); + else + return ResolveAttributeValue (attributes [orderedAttributes [i]] as string); } public override string GetAttribute (string name) { - return attributes [name] as string; + return attributes.ContainsKey (name) ? + ResolveAttributeValue (attributes [name] as string) : String.Empty; } public override string GetAttribute (string localName, string namespaceURI) @@ -335,10 +338,12 @@ namespace System.Xml string thisNamespaceURI = LookupNamespace (thisPrefix); if (namespaceURI == thisNamespaceURI) - return attributes [thisName] as string; + return attributes.ContainsKey (thisName) ? + ResolveAttributeValue (attributes [thisName] as string) : String.Empty; } } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns") - return attributes [thisName] as string; + return attributes.ContainsKey (thisName) ? + ResolveAttributeValue (attributes [thisName] as string) : String.Empty; } return String.Empty; @@ -353,7 +358,7 @@ namespace System.Xml [MonoTODO] bool IXmlLineInfo.HasLineInfo () { - throw new NotImplementedException (); + return false; } public override string LookupNamespace (string prefix) @@ -361,37 +366,49 @@ 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) { MoveToElement (); bool match = false; + if (attributes == null) return false; - if (attributeEnumerator == null) { + if (orderedAttributesEnumerator == null) { SaveProperties (); - attributeEnumerator = attributes.GetEnumerator (); + orderedAttributesEnumerator = orderedAttributes.GetEnumerator (); } - while (attributeEnumerator.MoveNext ()) { - if(name == attributeEnumerator.Key as string) { + while (orderedAttributesEnumerator.MoveNext ()) { + if(name == orderedAttributesEnumerator.Current as string) { match = true; break; } } if (match) { - string attname = attributeEnumerator.Key as string; - string value = attributeEnumerator.Value as string; + string value = attributes [name] as string; SetProperties ( XmlNodeType.Attribute, // nodeType - attname, // name + name, // name false, // isEmptyElement value, // value false // clearAttributes @@ -409,8 +426,8 @@ namespace System.Xml public override bool MoveToElement () { - if (attributeEnumerator != null) { - attributeEnumerator = null; + if (orderedAttributesEnumerator != null) { + orderedAttributesEnumerator = null; RestoreProperties (); return true; } @@ -429,14 +446,14 @@ namespace System.Xml if (attributes == null) return false; - if (attributeEnumerator == null) { + if (orderedAttributesEnumerator == null) { SaveProperties (); - attributeEnumerator = attributes.GetEnumerator (); + orderedAttributesEnumerator = orderedAttributes.GetEnumerator (); } - if (attributeEnumerator.MoveNext ()) { - string name = attributeEnumerator.Key as string; - string value = attributeEnumerator.Value as string; + if (orderedAttributesEnumerator.MoveNext ()) { + string name = orderedAttributesEnumerator.Current as string; + string value = attributes [name] as string; SetProperties ( XmlNodeType.Attribute, // nodeType name, // name @@ -461,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] @@ -500,10 +589,10 @@ namespace System.Xml while (startname != endname) { ReadContent (); - endname = this.Name; + endname = this.Name; } - xmlBuffer.Replace(currentTag.ToString (), ""); + xmlBuffer.Replace (currentTag.ToString (), ""); saveToXmlBuffer = false; string InnerXml = xmlBuffer.ToString (); xmlBuffer.Length = 0; @@ -514,21 +603,18 @@ namespace System.Xml [MonoTODO] public override string ReadOuterXml () { - // Still need a Well Formedness check. - // Will wait for Validating reader ;-) if (NodeType == XmlNodeType.Attribute) { - return Name+"=\""+Value+"\""; + return Name + "=\"" + Value.Replace ("\"", """) + "\""; } 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; @@ -551,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 + + #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 - // privates + #region Privates private XmlParserContext parserContext; @@ -564,6 +690,7 @@ namespace System.Xml private ReadState readState; private int depth; + private int elementDepth; private bool depthDown; private bool popScope; @@ -584,7 +711,8 @@ namespace System.Xml private bool saveIsEmptyElement; private Hashtable attributes; - private IDictionaryEnumerator attributeEnumerator; + private ArrayList orderedAttributes; + private IEnumerator orderedAttributesEnumerator; private bool returnEntityReference; private string entityReferenceName; @@ -602,12 +730,20 @@ namespace System.Xml private StringBuilder xmlBuffer; // This is for Read(Inner|Outer)Xml private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml 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 () { readState = ReadState.Initial; - depth = -1; + depth = 0; depthDown = false; popScope = false; @@ -620,7 +756,8 @@ namespace System.Xml value = String.Empty; attributes = new Hashtable (); - attributeEnumerator = null; + orderedAttributes = new ArrayList (); + orderedAttributesEnumerator = null; returnEntityReference = false; entityReferenceName = String.Empty; @@ -653,6 +790,7 @@ namespace System.Xml this.name = name; this.isEmptyElement = isEmptyElement; this.value = value; + this.elementDepth = depth; if (clearAttributes) ClearAttributes (); @@ -695,24 +833,48 @@ namespace System.Xml private void AddAttribute (string name, string value) { attributes.Add (name, value); + orderedAttributes.Add (name); } private void ClearAttributes () { - if (attributes.Count > 0) + if (attributes.Count > 0) { attributes.Clear (); + orderedAttributes.Clear (); + } - attributeEnumerator = null; + orderedAttributesEnumerator = null; } 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; + } else { + column++; + } if (saveToXmlBuffer) { xmlBuffer.Append ((char) ch); } @@ -725,28 +887,42 @@ namespace System.Xml // element or text outside of the document element. private bool ReadContent () { - bool more = false; currentTag.Length = 0; if (popScope) { parserContext.NamespaceManager.PopScope (); popScope = false; } - if (depthDown) - --depth; - if (returnEntityReference) { - ++depth; SetEntityReferenceProperties (); - more = true; } else { switch (PeekChar ()) { case '<': ReadChar (); ReadTag (); - more = true; break; + case '\r': + if (whitespaceHandling == WhitespaceHandling.All || + whitespaceHandling == WhitespaceHandling.Significant) + return ReadWhitespace (); + + ReadChar (); + return ReadContent (); + case '\n': + if (whitespaceHandling == WhitespaceHandling.All || + whitespaceHandling == WhitespaceHandling.Significant) + return ReadWhitespace (); + + ReadChar (); + return ReadContent (); + case ' ': + if (whitespaceHandling == WhitespaceHandling.All || + whitespaceHandling == WhitespaceHandling.Significant) + return ReadWhitespace (); + + SkipWhitespace (); + return ReadContent (); case -1: readState = ReadState.EndOfFile; SetProperties ( @@ -756,16 +932,13 @@ namespace System.Xml String.Empty, // value true // clearAttributes ); - more = false; break; default: - ReadText (); - more = true; + ReadText (true); break; } } - - return more; + return this.ReadState != ReadState.EndOfFile; } private void SetEntityReferenceProperties () @@ -829,8 +1002,6 @@ namespace System.Xml Expect ('>'); - ++depth; - SetProperties ( XmlNodeType.Element, // nodeType name, // name @@ -838,6 +1009,12 @@ namespace System.Xml String.Empty, // value false // clearAttributes ); + + if (!depthDown) + ++depth; + else + depthDown = false; + } // The reader is positioned on the first character @@ -905,9 +1082,10 @@ namespace System.Xml // The reader is positioned on the first character // of the text. - private void ReadText () + private void ReadText (bool cleanValue) { - valueLength = 0; + if (cleanValue) + valueLength = 0; int ch = PeekChar (); @@ -923,14 +1101,8 @@ namespace System.Xml } if (returnEntityReference && valueLength == 0) { - ++depth; SetEntityReferenceProperties (); } else { - if (depth >= 0) { - ++depth; - depthDown = true; - } - SetProperties ( XmlNodeType.Text, // nodeType String.Empty, // name @@ -1062,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); @@ -1073,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 (); @@ -1087,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: @@ -1099,12 +1275,15 @@ namespace System.Xml } ReadChar (); // quoteChar + AppendValueChar (quoteChar); return CreateValueString (); } // The reader is positioned on the first character // of the target. + // + // Now it also reads XmlDeclaration, this method name became improper... private void ReadProcessingInstruction () { string target = ReadName (); @@ -1123,7 +1302,16 @@ namespace System.Xml AppendValueChar ((char)ch); } +/* 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 @@ -1141,20 +1329,18 @@ namespace System.Xml switch (ch) { case '-': - Expect ('-'); - Expect ('-'); + Expect ("--"); ReadComment (); break; case '[': ReadChar (); - Expect ('C'); - Expect ('D'); - Expect ('A'); - Expect ('T'); - Expect ('A'); - Expect ('['); + Expect ("CDATA["); ReadCDATA (); break; + case 'D': + Expect ("DOCTYPE"); + ReadDoctypeDecl (); + break; } } @@ -1214,8 +1400,6 @@ namespace System.Xml AppendValueChar ((char)ch); } - ++depth; - SetProperties ( XmlNodeType.CDATA, // nodeType String.Empty, // name @@ -1225,6 +1409,189 @@ namespace System.Xml ); } + // The reader is positioned on the first character after + // the leading ''); + + parserContext.InternalSubset = xmlBuffer.ToString (); + + // set properties for node + SetProperties ( + XmlNodeType.DocumentType, // nodeType + doctypeName, // name + false, // isEmptyElement + parserContext.InternalSubset, // value + true // clearAttributes + ); + } + + // Read any one of following: + // elementdecl, AttlistDecl, EntityDecl, NotationDecl, + // PI, Comment, Parameter Entity, or doctype termination char(']') + // + // returns a node of some nodeType or null, setting nodeType. + // (if None then ']' was found.) + private void ReadDTDInternalSubset() + { + SkipWhitespace (); + switch(ReadChar ()) + { + case ']': + nodeType = XmlNodeType.None; + break; + case '%': + string peName = ReadName (); + Expect (';'); + nodeType = XmlNodeType.EntityReference; // It's chating a bit;-) + break; + case '<': + switch(ReadChar ()) + { + case '?': + ReadProcessingInstruction (); + break; + case '!': + switch(ReadChar ()) + { + case '-': + Expect ('-'); + ReadComment (); + break; + case 'E': + switch(ReadChar ()) + { + case 'N': + Expect ("TITY"); + ReadEntityDecl (); + break; + case 'L': + Expect ("EMENT"); + ReadElementDecl (); + break; + default: + throw new XmlException ("Syntax Error after ''); + } + + private void ReadEntityDecl() + { + while(ReadChar () != '>'); + } + + private void ReadAttListDecl() + { + while(ReadChar () != '>'); + } + + private void ReadNotationDecl() + { + while(ReadChar () != '>'); + } + + // The reader is positioned on the first 'S' of "SYSTEM". + private string ReadSystemLiteral (bool expectSYSTEM) + { + 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."); + } + saveToXmlBuffer = false; + xmlBuffer.Remove (xmlBuffer.Length-1, 1); // cut quoteChar + return xmlBuffer.ToString (); + } + + private string ReadPubidLiteral() + { + 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)) + throw new XmlException("character '" + (char)c + "' not allowed for PUBLIC ID"); + } + ReadChar(); // skips quoteChar + xmlBuffer.Remove (xmlBuffer.Length-1, 1); // cut quoteChar + saveToXmlBuffer = false; + return xmlBuffer.ToString (); + } + // The reader is positioned on the first character // of the name. private string ReadName () @@ -1260,11 +1627,131 @@ namespace System.Xml } } + private void Expect (string expected) + { + int len = expected.Length; + for(int i=0; i< len; i++) + Expect (expected[i]); + } + // Does not consume the first non-whitespace character. private void SkipWhitespace () { + //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None while (XmlChar.IsWhitespace (PeekChar ())) ReadChar (); } + + private bool ReadWhitespace () + { + valueLength = 0; + int ch = PeekChar (); + do { + AppendValueChar (ReadChar ()); + } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch)); + + if (ch != -1 && ch != '<') + ReadText (false); + else + SetProperties (XmlNodeType.Whitespace, + String.Empty, + false, + CreateValueString (), + true); + + 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; &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 } }