X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.XML%2FSystem.Xml%2FXmlTextReader.cs;h=f1d5b59e8b04e5f2364e7c63c64d26bd4fe4522d;hb=073481b554c5a73f6954392c6f86a701db817d04;hp=fdcad52628ecea7bbc66b593e3ccfc3fc8a1d19b;hpb=469f0f2e8a9c3f9bf703baa4e15d22ff472c4122;p=mono.git diff --git a/mcs/class/System.XML/System.Xml/XmlTextReader.cs b/mcs/class/System.XML/System.Xml/XmlTextReader.cs index fdcad52628e..f1d5b59e8b0 100644 --- a/mcs/class/System.XML/System.Xml/XmlTextReader.cs +++ b/mcs/class/System.XML/System.Xml/XmlTextReader.cs @@ -9,12 +9,30 @@ // (C) 2001, 2002 Jason Diamond http://injektilo.org/ // -// FIXME: // -// NameTables aren't being used completely yet. +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +// FIXME: // -// Some thought needs to be given to performance. There's too many -// strings being allocated. +// Some thought needs to be given to performance. // // If current node is on an Attribute, Prefix might be null, and // in several fields which uses XmlReader, it should be considered. @@ -22,16 +40,21 @@ using System; using System.Collections; -using System.Collections.Specialized; +using System.Globalization; using System.IO; +using System.Security.Policy; using System.Text; using System.Xml.Schema; using Mono.Xml; -using Mono.Xml.Native; namespace System.Xml { +#if NET_2_0 + public class XmlTextReader : XmlReader, + IXmlLineInfo, IXmlNamespaceResolver +#else public class XmlTextReader : XmlReader, IXmlLineInfo +#endif { #region Constructors @@ -75,8 +98,14 @@ namespace System.Xml } public XmlTextReader (string url, XmlNameTable nt) - : this (url, new XmlStreamReader (url, null, null), nt) { + Uri uri = resolver.ResolveUri (null, url); + Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream; + XmlParserContext ctx = new XmlParserContext (nt, + new XmlNamespaceManager (nt), + String.Empty, + XmlSpace.None); + this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document); } public XmlTextReader (TextReader input, XmlNameTable nt) @@ -129,13 +158,32 @@ namespace System.Xml get { return parserContext.BaseURI; } } +#if NET_2_0 + public override bool CanResolveEntity { + get { return true; } + } + +#endif + + internal bool CharacterChecking { + get { return checkCharacters && normalization; } + set { checkCharacters = value; } + } + + // for XmlReaderSettings.CloseInput support + internal bool CloseInput { + get { return closeInput; } + set { closeInput = value; } + } + public override int Depth { get { + int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element ? 0 : -1; if (currentAttributeValue >= 0) - return elementDepth + 2; // inside attribute value. + return nodeTypeMod + elementDepth + 2; // inside attribute value. else if (currentAttribute >= 0) - return elementDepth + 1; + return nodeTypeMod + elementDepth + 1; return elementDepth; } } @@ -144,88 +192,77 @@ namespace System.Xml { get { return parserContext.Encoding; } } +#if NET_2_0 + [MonoTODO] + public EntityHandling EntityHandling { + get { return entityHandling; } + set { entityHandling = value; } + } +#endif + + public override bool EOF { + get { return readState == ReadState.EndOfFile; } + } - public override bool EOF - { - get - { - return - readState == ReadState.EndOfFile || - readState == ReadState.Closed; - } +#if NET_2_0 + [MonoTODO] + public override Evidence Evidence { + get { return base.Evidence; } } +#endif - public override bool HasValue - { - get { - if (this.valueBuilderAvailable) - return valueBuilder.Length != 0; - else - return cursorToken.Value != null; - } + public override bool HasValue { + get { return cursorToken.Value != null; } } - public override bool IsDefault - { - get - { - // XmlTextReader does not expand default attributes. - return false; - } + public override bool IsDefault { + // XmlTextReader does not expand default attributes. + get { return false; } } - public override bool IsEmptyElement - { + public override bool IsEmptyElement { get { return cursorToken.IsEmptyElement; } } - public override string this [int i] - { + public override string this [int i] { get { return GetAttribute (i); } } - public override string this [string name] - { + public override string this [string name] { get { return GetAttribute (name); } } - public override string this [string localName, string namespaceName] - { + public override string this [string localName, string namespaceName] { get { return GetAttribute (localName, namespaceName); } } - public int LineNumber - { + public int LineNumber { get { if (useProceedingLineInfo) - return currentInput.LineNumber; + return line; else return cursorToken.LineNumber; } } - public int LinePosition - { + public int LinePosition { get { if (useProceedingLineInfo) - return currentInput.LinePosition; + return column; else return cursorToken.LinePosition; } } - public override string LocalName - { + public override string LocalName { get { return cursorToken.LocalName; } } - public override string Name - { + public override string Name { get { return cursorToken.Name; } } - public bool Namespaces - { + public bool Namespaces { get { return namespaces; } set { if (readState != ReadState.Initial) @@ -234,66 +271,66 @@ namespace System.Xml } } - public override string NamespaceURI - { + public override string NamespaceURI { get { return cursorToken.NamespaceURI; } } - public override XmlNameTable NameTable - { + public override XmlNameTable NameTable { get { return parserContext.NameTable; } } - public override XmlNodeType NodeType - { + public override XmlNodeType NodeType { get { return cursorToken.NodeType; } } - [MonoTODO] - public bool Normalization - { + public bool Normalization { get { return normalization; } set { normalization = value; } } - public override string Prefix - { + public override string Prefix { get { return cursorToken.Prefix; } } - public override char QuoteChar - { +#if NET_2_0 + public bool ProhibitDtd { + get { return prohibitDtd; } + set { prohibitDtd = value; } + } +#endif + + public override char QuoteChar { get { return cursorToken.QuoteChar; } } - public override ReadState ReadState - { + public override ReadState ReadState { get { return readState; } } - public override string Value - { +#if NET_2_0 + public override XmlReaderSettings Settings { + get { return base.Settings; } + } +#endif + + public override string Value { get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; } } - public WhitespaceHandling WhitespaceHandling - { + public WhitespaceHandling WhitespaceHandling { get { return whitespaceHandling; } set { whitespaceHandling = value; } } - public override string XmlLang - { + public override string XmlLang { get { return parserContext.XmlLang; } } - public XmlResolver XmlResolver - { + public XmlResolver XmlResolver { set { resolver = value; } } - public override XmlSpace XmlSpace - { + public override XmlSpace XmlSpace { get { return parserContext.XmlSpace; } } @@ -304,18 +341,17 @@ namespace System.Xml public override void Close () { readState = ReadState.Closed; - foreach (XmlParserInput input in parserInputStack.ToArray ()) - input.Close (); - this.currentInput.Close (); cursorToken.Clear (); currentToken.Clear (); attributeCount = 0; + if (closeInput && reader != null) + reader.Close (); } public override string GetAttribute (int i) { - if (i > attributeCount) + if (i >= attributeCount) throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount"); else { return attributeTokens [i].Value; @@ -356,22 +392,55 @@ namespace System.Xml return attributeTokens [idx].Value; } - [MonoTODO] +#if NET_2_0 + public IDictionary GetNamespacesInScope (XmlNamespaceScope scope) + { + return parserContext.NamespaceManager.GetNamespacesInScope (scope); + } +#endif + public TextReader GetRemainder () { - throw new NotImplementedException (); + if (peekCharsIndex == peekCharsLength) + return reader; + return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ()); } +#if NET_2_0 + public bool HasLineInfo () +#else bool IXmlLineInfo.HasLineInfo () +#endif { return true; } public override string LookupNamespace (string prefix) { - return parserContext.NamespaceManager.LookupNamespace (prefix); + return LookupNamespace (prefix, false); + } + +#if NET_2_0 + public override string LookupNamespace (string prefix, bool atomizedName) +#else + internal override string LookupNamespace (string prefix, bool atomizedName) +#endif + { + return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName); + } + +#if NET_2_0 + string IXmlNamespaceResolver.LookupPrefix (string ns) + { + return LookupPrefix (ns, false); } + public string LookupPrefix (string ns, bool atomizedName) + { + return parserContext.NamespaceManager.LookupPrefix (ns, atomizedName); + } +#endif + public override void MoveToAttribute (int i) { if (i >= attributeCount) @@ -408,6 +477,9 @@ namespace System.Xml if (currentToken == null) // for attribute .ctor() return false; + if (cursorToken == currentToken) + return false; + if (currentAttribute >= 0) { currentAttribute = -1; currentAttributeValue = -1; @@ -456,8 +528,8 @@ namespace System.Xml bool more = false; readState = ReadState.Interactive; - currentLinkedNodeLineNumber = currentInput.LineNumber; - currentLinkedNodeLinePosition = currentInput.LinePosition; + currentLinkedNodeLineNumber = line; + currentLinkedNodeLinePosition = column; useProceedingLineInfo = true; cursorToken = currentToken; @@ -466,16 +538,19 @@ namespace System.Xml currentToken.Clear (); // It was moved from end of ReadStartTag (). - if (depthUp) + if (depthUp) { ++depth; - depthUp = false; + depthUp = false; + } - more = ReadContent (); + if (shouldSkipUntilEndTag) { + shouldSkipUntilEndTag = false; + return ReadUntilEndTag (); + } - if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement)) - currentState = XmlNodeType.EndElement; - if (maybeTextDecl != 0) - maybeTextDecl--; + base64CacheStartsAt = -1; + + more = ReadContent (); if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement) throw new XmlException ("Document element did not appear."); @@ -505,132 +580,282 @@ namespace System.Xml return false; } - [MonoTODO] + private int SkipIgnorableBase64Chars (char [] chars, int charsLength, int i) + { + while (chars [i] == '=' || XmlChar.IsWhitespace (chars [i])) + if (charsLength == ++i) + break; + return i; + } + public int ReadBase64 (byte [] buffer, int offset, int length) { - throw new NotImplementedException (); + if (offset < 0) + throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer."); + else if (length < 0) + throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer."); + else if (buffer.Length < offset + length) + throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length."); + + if (length == 0) // It does not raise an error. + return 0; + + int bufIndex = offset; + int bufLast = offset + length; + + if (base64CacheStartsAt >= 0) { + for (int i = base64CacheStartsAt; i < 3; i++) { + buffer [bufIndex++] = base64Cache [base64CacheStartsAt++]; + if (bufIndex == bufLast) + return bufLast - offset; + } + } + + for (int i = 0; i < 3; i++) + base64Cache [i] = 0; + base64CacheStartsAt = -1; + + int max = (int) System.Math.Ceiling (4.0 / 3 * length); + int additional = max % 4; + if (additional > 0) + max += 4 - additional; + char [] chars = new char [max]; + int charsLength = ReadChars (chars, 0, max); + + byte b = 0; + byte work = 0; + bool loop = true; + for (int i = 0; i < charsLength - 3; i++) { + if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength) + break; + b = (byte) (GetBase64Byte (chars [i]) << 2); + if (bufIndex < bufLast) + buffer [bufIndex] = b; + else { + if (base64CacheStartsAt < 0) + base64CacheStartsAt = 0; + base64Cache [0] = b; + } + // charsLength mod 4 might not equals to 0. + if (++i == charsLength) + break; + if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength) + break; + b = GetBase64Byte (chars [i]); + work = (byte) (b >> 4); + if (bufIndex < bufLast) { + buffer [bufIndex] += work; + bufIndex++; + } + else + base64Cache [0] += work; + + work = (byte) ((b & 0xf) << 4); + if (bufIndex < bufLast) { + buffer [bufIndex] = work; + } + else { + if (base64CacheStartsAt < 0) + base64CacheStartsAt = 1; + base64Cache [1] = work; + } + + if (++i == charsLength) + break; + if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength) + break; + b = GetBase64Byte (chars [i]); + work = (byte) (b >> 2); + if (bufIndex < bufLast) { + buffer [bufIndex] += work; + bufIndex++; + } + else + base64Cache [1] += work; + + work = (byte) ((b & 3) << 6); + if (bufIndex < bufLast) + buffer [bufIndex] = work; + else { + if (base64CacheStartsAt < 0) + base64CacheStartsAt = 2; + base64Cache [2] = work; + } + if (++i == charsLength) + break; + if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength) + break; + work = GetBase64Byte (chars [i]); + if (bufIndex < bufLast) { + buffer [bufIndex] += work; + bufIndex++; + } + else + base64Cache [2] += work; + } + return System.Math.Min (bufLast - offset, bufIndex - offset); } - [MonoTODO] public int ReadBinHex (byte [] buffer, int offset, int length) { - throw new NotImplementedException (); + if (offset < 0) + throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer."); + else if (length < 0) + throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer."); + else if (buffer.Length < offset + length) + throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length."); + + if (length == 0) + return 0; + + char [] chars = new char [length * 2]; + int charsLength = ReadChars (chars, 0, length * 2); + return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer); } - [MonoTODO] public int ReadChars (char [] buffer, int offset, int length) { - throw new NotImplementedException (); + return ReadCharsInternal (buffer, offset, length); } -#if NET_1_0 +#if NET_2_0 + public override string ReadString () + { + return ReadStringInternal (); + } +#elif NET_1_1 +#else public override string ReadInnerXml () { - if (readState != ReadState.Interactive) - return String.Empty; - - switch (NodeType) { - case XmlNodeType.Attribute: - return value.Substring (1, value.Length - 2); - case XmlNodeType.Element: - if (IsEmptyElement) - return String.Empty; - - int startDepth = depth; - - if (innerXmlBuilder == null) - innerXmlBuilder = new StringBuilder (); - innerXmlBuilder.Length = 0; - bool loop = true; - do { - Read (); - if (NodeType ==XmlNodeType.None) - throw new XmlException ("unexpected end of xml."); - else if (NodeType == XmlNodeType.EndElement && depth == startDepth) { - loop = false; - Read (); - } - else - innerXmlBuilder.Append (currentTag); - } while (loop); - string xml = innerXmlBuilder.ToString (); - innerXmlBuilder.Length = 0; - return xml; - case XmlNodeType.None: - // MS document is incorrect. Seems not to progress. - return String.Empty; - default: - Read (); - return String.Empty; - } + return ReadInnerXmlInternal (); } public override string ReadOuterXml () { - if (readState != ReadState.Interactive) - return String.Empty; - - switch (NodeType) { - case XmlNodeType.Attribute: - // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial). - return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ()); - case XmlNodeType.Element: - bool isEmpty = IsEmptyElement; - string startTag = currentTag.ToString (); - string name = Name; - - if (NodeType == XmlNodeType.Element && !isEmpty) - return String.Format ("{0}{1}", startTag, ReadInnerXml (), name); - else - return currentTag.ToString (); - case XmlNodeType.None: - // MS document is incorrect. Seems not to progress. - return String.Empty; - default: - Read (); - return String.Empty; - } + return ReadOuterXmlInternal (); } -#endif public override string ReadString () { return ReadStringInternal (); } +#endif public void ResetState () { + throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment."); Init (); } +#if NET_2_0 + [MonoTODO] + public override bool ReadValueAsBoolean () + { + return base.ReadValueAsBoolean (); + } + + [MonoTODO] + public override DateTime ReadValueAsDateTime () + { + return base.ReadValueAsDateTime (); + } + + [MonoTODO] + public override decimal ReadValueAsDecimal () + { + return base.ReadValueAsDecimal (); + } + + [MonoTODO] + public override double ReadValueAsDouble () + { + return base.ReadValueAsDouble (); + } + + [MonoTODO] + public override int ReadValueAsInt32 () + { + return base.ReadValueAsInt32 (); + } + + [MonoTODO] + public override long ReadValueAsInt64 () + { + return base.ReadValueAsInt64 (); + } + + [MonoTODO] + public override ICollection ReadValueAsList () + { + return base.ReadValueAsList (); + } + + [MonoTODO] + public override float ReadValueAsSingle () + { + return base.ReadValueAsSingle (); + } + + [MonoTODO] + public override string ReadValueAsString () + { + return ReadString (); + } + + [MonoTODO] + public override object ReadValueAs (Type type) + { + return base.ReadValueAs (type); + } + + [MonoTODO] + public override object ReadValueAs (Type type, IXmlNamespaceResolver resolver) + { + return base.ReadValueAs (type, resolver); + } +#endif + public override void ResolveEntity () { // XmlTextReader does not resolve entities. throw new InvalidOperationException ("XmlTextReader cannot resolve external entities."); } +#if NET_2_0 + [MonoTODO ("Implement for performance reason")] + public override void Skip () + { + base.Skip (); + } +#endif #endregion #region Internals // Parsed DTD Objects +#if DTD_HANDLE_EVENTS + internal event ValidationEventHandler ValidationEventHandler; +#endif + internal DTDObjectModel DTD { get { return parserContext.Dtd; } } - internal bool MaybeTextDecl { - set { if (value) this.maybeTextDecl = 2; } + internal XmlResolver Resolver { + get { return resolver; } } #endregion #region Privates internal class XmlTokenInfo { - public XmlTokenInfo (XmlTextReader xtr) + public XmlTokenInfo (XmlTextReader xtr, bool isPrimaryToken) { + this.isPrimaryToken = isPrimaryToken; Reader = xtr; Clear (); } + bool isPrimaryToken; string valueCache; protected XmlTextReader Reader; @@ -650,15 +875,19 @@ namespace System.Xml get { if (valueCache != null) return valueCache; - else if (Reader.valueBuilderAvailable) { - valueCache = Reader.valueBuilder.ToString (); + switch (NodeType) { + case XmlNodeType.Text: + case XmlNodeType.SignificantWhitespace: + case XmlNodeType.Whitespace: + case XmlNodeType.Comment: + case XmlNodeType.CDATA: + case XmlNodeType.ProcessingInstruction: + valueCache = Reader.CreateValueString (); return valueCache; } - return valueCache; - } - set { - valueCache = value; + return null; } + set { valueCache = value; } } public virtual void Clear () @@ -674,28 +903,40 @@ namespace System.Xml internal virtual void FillNames () { if (Reader.Namespaces) { - int indexOfColon = Name.IndexOf (':'); + int indexOfColon = -1; + switch (NodeType) { + case XmlNodeType.Attribute: + case XmlNodeType.Element: + case XmlNodeType.EndElement: + indexOfColon = Name.IndexOf (':'); + break; + } if (indexOfColon == -1) { Prefix = String.Empty; LocalName = Name; } else { - Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon)); - LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1)); + // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3% + // However, this might be reverted if NameTable is got improved. + char [] nameArr = Name.ToCharArray (); + Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon); + LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1); +// Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon)); +// LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1)); } // NamespaceURI switch (NodeType) { case XmlNodeType.Attribute: - if (Prefix == string.Empty) + if (Prefix.Length == 0) NamespaceURI = string.Empty; else - NamespaceURI = Reader.LookupNamespace (Prefix); + NamespaceURI = Reader.LookupNamespace (Prefix, true); break; case XmlNodeType.Element: case XmlNodeType.EndElement: - NamespaceURI = Reader.LookupNamespace (Prefix); + NamespaceURI = Reader.LookupNamespace (Prefix, true); break; default: NamespaceURI = ""; @@ -711,7 +952,7 @@ namespace System.Xml internal class XmlAttributeTokenInfo : XmlTokenInfo { public XmlAttributeTokenInfo (XmlTextReader reader) - : base (reader) + : base (reader, false) { NodeType = XmlNodeType.Attribute; } @@ -719,40 +960,49 @@ namespace System.Xml public int ValueTokenStartIndex; public int ValueTokenEndIndex; string valueCache; + bool cachedNormalization; + StringBuilder tmpBuilder = new StringBuilder (); public override string Value { get { + if (cachedNormalization != Reader.Normalization) + valueCache = null; if (valueCache != null) return valueCache; + + cachedNormalization = Reader.Normalization; + // An empty value should return String.Empty. if (ValueTokenStartIndex == ValueTokenEndIndex) { XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex]; - if (ti.NodeType == XmlNodeType.Text) - valueCache = ti.Value; - else + if (ti.NodeType == XmlNodeType.EntityReference) valueCache = String.Concat ("&", ti.Name, ";"); + else + valueCache = ti.Value; + if (cachedNormalization) + NormalizeSpaces (); return valueCache; } - StringBuilder sb = new StringBuilder (); + tmpBuilder.Length = 0; for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) { XmlTokenInfo ti = Reader.attributeValueTokens [i]; if (ti.NodeType == XmlNodeType.Text) - sb.Append (ti.Value); + tmpBuilder.Append (ti.Value); else { - sb.Append ('&'); - sb.Append (ti.Name); - sb.Append (';'); + tmpBuilder.Append ('&'); + tmpBuilder.Append (ti.Name); + tmpBuilder.Append (';'); } } - valueCache = sb.ToString (); - + valueCache = tmpBuilder.ToString (); + if (cachedNormalization) + NormalizeSpaces (); return valueCache; } - set { - valueCache = value; - } + + set { valueCache = value; } } public override void Clear () @@ -769,6 +1019,26 @@ namespace System.Xml if (Prefix == "xmlns" || Name == "xmlns") NamespaceURI = XmlNamespaceManager.XmlnsXmlns; } + + private void NormalizeSpaces () + { + tmpBuilder.Length = 0; + for (int i = 0; i < valueCache.Length; i++) + switch (valueCache [i]) { + case '\r': + if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n') + i++; + goto case '\n'; + case '\t': + case '\n': + tmpBuilder.Append (' '); + break; + default: + tmpBuilder.Append (valueCache [i]); + break; + } + valueCache = tmpBuilder.ToString (); + } } private XmlTokenInfo cursorToken; @@ -783,8 +1053,6 @@ namespace System.Xml private XmlParserContext parserContext; - private XmlParserInput currentInput; - private Stack parserInputStack; private ReadState readState; private int depth; @@ -792,39 +1060,45 @@ namespace System.Xml private bool depthUp; private bool popScope; - private Stack elementStack; + + private string [] elementNames; + int elementNameStackPos; + private bool allowMultipleRoot; private bool isStandalone; - private StringBuilder valueBuilder; - private bool valueBuilderAvailable = false; - private bool returnEntityReference; private string entityReferenceName; private char [] nameBuffer; private int nameLength; private int nameCapacity; - private const int initialNameCapacity = 256; + private const int initialNameCapacity = 32; + + private char [] valueBuffer; + private int valueLength; + private int valueCapacity; + private const int initialValueCapacity = 256; + + private char [] currentTagBuffer; + private int currentTagLength; + private int currentTagCapacity; + private const int initialCurrentTagCapacity = 256; - private StringBuilder valueBuffer; + private TextReader reader; + private char [] peekChars; + private int peekCharsIndex; + private int peekCharsLength; + private const int peekCharCapacity = 1024; + + private int line; + private int column; private int currentLinkedNodeLineNumber; private int currentLinkedNodeLinePosition; private bool useProceedingLineInfo; - // A buffer for ReadContent for ReadOuterXml - private StringBuilder currentTag { - get { - return currentInput.CurrentMarkup; - } - } - - // Parameter entity placeholder - private Hashtable parameterEntities; - private int dtdIncludeSect; - private XmlNodeType startNodeType; // State machine attribute. // XmlDeclaration: after the first node. @@ -832,43 +1106,83 @@ namespace System.Xml // Element: inside document element // EndElement: after document element private XmlNodeType currentState; - private int maybeTextDecl; - private XmlResolver resolver = new XmlUrlResolver (); + // For ReadChars()/ReadBase64()/ReadBinHex() + private bool shouldSkipUntilEndTag; + private byte [] base64Cache = new byte [3]; + private int base64CacheStartsAt; // These values are never re-initialized. private bool namespaces = true; private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All; + private XmlResolver resolver = new XmlUrlResolver (); private bool normalization = false; + private bool checkCharacters; + private bool prohibitDtd = false; + private bool closeInput = true; + private EntityHandling entityHandling; // 2.0 + private void Init () { + currentToken = new XmlTokenInfo (this, true); + cursorToken = currentToken; + currentAttribute = -1; + currentAttributeValue = -1; + attributeCount = 0; + readState = ReadState.Initial; - currentState = XmlNodeType.None; - maybeTextDecl = 0; allowMultipleRoot = false; depth = 0; + elementDepth = 0; depthUp = false; - popScope = false; - parserInputStack = new Stack (); - elementStack = new Stack(); - currentAttribute = -1; - currentAttributeValue = -1; + popScope = allowMultipleRoot = false; + elementNames = new string [10]; + elementNameStackPos = 0; + isStandalone = false; returnEntityReference = false; entityReferenceName = String.Empty; nameBuffer = new char [initialNameCapacity]; nameLength = 0; nameCapacity = initialNameCapacity; - - valueBuffer = new StringBuilder (512); - parameterEntities = new Hashtable (); - currentToken = new XmlTokenInfo (this); - cursorToken = currentToken; + valueBuffer = new char [initialValueCapacity]; + valueLength = 0; + valueCapacity = initialValueCapacity; + + currentTagBuffer = new char [initialCurrentTagCapacity]; + currentTagLength = 0; + currentTagCapacity = initialCurrentTagCapacity; + + peekCharsIndex = 0; + peekCharsLength = 0; + if (peekChars == null) + peekChars = new char [peekCharCapacity]; + + line = 1; + column = 1; + currentTagLength = 0; + + currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0; + useProceedingLineInfo = false; + + currentState = XmlNodeType.None; + + shouldSkipUntilEndTag = false; + base64CacheStartsAt = -1; + + checkCharacters = true; +#if NET_2_0 + if (Settings != null) + checkCharacters = Settings.CheckCharacters; +#endif + prohibitDtd = false; + closeInput = true; + entityHandling = EntityHandling.ExpandCharEntities; } private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType) @@ -883,17 +1197,24 @@ namespace System.Xml XmlSpace.None); } - if (url != null && url != String.Empty) { - string path = Path.GetFullPath ("./a"); - Uri uri = new Uri (new Uri (path), url); + if (url != null && url.Length > 0) { + Uri uri = null; + try { + uri = new Uri (url); + } catch (Exception) { + string path = Path.GetFullPath ("./a"); + uri = new Uri (new Uri (path), url); + } parserContext.BaseURI = uri.ToString (); } Init (); + reader = fragment; + switch (fragType) { case XmlNodeType.Attribute: - fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", """)); + reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", """)); break; case XmlNodeType.Element: currentState = XmlNodeType.Element; @@ -904,9 +1225,30 @@ namespace System.Xml default: throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType)); } + } + +#if NET_2_0 + [MonoTODO ("Test")] + internal ConformanceLevel Conformance { + set { + if (value == ConformanceLevel.Fragment) { + currentState = XmlNodeType.Element; + allowMultipleRoot = true; + } + } + } + + internal void AdjustLineInfoOffset (int lineNumberOffset, int linePositionOffset) + { + line += lineNumberOffset; + column += linePositionOffset; + } - this.currentInput = new XmlParserInput (fragment, url); + internal void SetNameTable (XmlNameTable nameTable) + { + parserContext.NameTable = nameTable; } +#endif // Use this method rather than setting the properties // directly so that all the necessary properties can @@ -933,7 +1275,6 @@ namespace System.Xml string value, bool clearAttributes) { - this.valueBuilderAvailable = false; token.Clear (); token.NodeType = nodeType; token.Name = name; @@ -947,17 +1288,6 @@ namespace System.Xml token.FillNames (); } - private void SetProperties ( - XmlNodeType nodeType, - string name, - bool isEmptyElement, - bool clearAttributes, - StringBuilder value) { - SetProperties (nodeType, name, isEmptyElement, (string)null, clearAttributes); - this.valueBuilderAvailable = true; - this.valueBuilder = value; - } - private void ClearAttributes () { for (int i = 0; i < attributeCount; i++) @@ -969,12 +1299,59 @@ namespace System.Xml private int PeekChar () { - return currentInput.PeekChar (); + if (peekCharsLength == peekCharsIndex) { + if (!ReadTextReader ()) + return -1; + return PeekChar (); + } + else { + char c = peekChars [peekCharsIndex]; + if (c != 0) return c; + else return -1; + } } private int ReadChar () { - return currentInput.ReadChar (); + int ch; + + if (peekCharsLength == peekCharsIndex) { + if (!ReadTextReader ()) + return -1; + return ReadChar (); + } + ch = peekChars [peekCharsIndex++]; + + if (ch == '\n') { + line++; + column = 1; + } else if (ch == 0) { + return -1; + } else { + column++; + } + if (currentState != XmlNodeType.Element) + AppendCurrentTagChar (ch); + return ch; + } + + private bool ReadTextReader () + { + peekCharsIndex = 0; + peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity); + if (peekCharsLength == 0) + return false; + return true; + } + + private string ExpandSurrogateChar (int ch) + { + if (ch < Char.MaxValue) + return ((char) ch).ToString (); + else { + char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)}; + return new string (tmp); + } } // This should really keep track of some state so @@ -982,7 +1359,7 @@ namespace System.Xml // element or text outside of the document element. private bool ReadContent () { - currentTag.Length = 0; + currentTagLength = 0; if (popScope) { parserContext.NamespaceManager.PopScope (); popScope = false; @@ -991,64 +1368,65 @@ namespace System.Xml if (returnEntityReference) SetEntityReferenceProperties (); else { - switch (PeekChar ()) { - case '<': - ReadChar (); - ReadTag (); - break; - case '\r': goto case ' '; - case '\n': goto case ' '; - case '\t': goto case ' '; - case ' ': - if (whitespaceHandling == WhitespaceHandling.All || - whitespaceHandling == WhitespaceHandling.Significant) - ReadWhitespace (); - else { - SkipWhitespace (); - return ReadContent (); - } - break; - case -1: - if (depth > 0) - throw new XmlException ("unexpected end of file. Current depth is " + depth); + int c = PeekChar (); + if (c == -1) { readState = ReadState.EndOfFile; + ClearValueBuffer (); SetProperties ( XmlNodeType.None, // nodeType String.Empty, // name false, // isEmptyElement - (string) null, // value + null, // value true // clearAttributes ); + if (depth > 0) + throw new XmlException ("unexpected end of file. Current depth is " + depth); + return false; - default: - ReadText (true); - break; + } else { + switch ((char) c) { + case '<': + ReadChar (); + ReadTag (); + break; + case '\r': goto case ' '; + case '\n': goto case ' '; + case '\t': goto case ' '; + case ' ': + if (whitespaceHandling == WhitespaceHandling.All || + whitespaceHandling == WhitespaceHandling.Significant) + ReadWhitespace (); + else { + SkipWhitespace (); + return ReadContent (); + } + break; + default: + ReadText (true); + break; + } } } - if (NodeType == XmlNodeType.XmlDeclaration && maybeTextDecl == 1) - return ReadContent (); return this.ReadState != ReadState.EndOfFile; } private void SetEntityReferenceProperties () { -/* - if (resolver != null) { - if (DTD == null) - throw new XmlException (this as IXmlLineInfo, - "Entity reference is not allowed without document type declaration."); - else if((!DTD.InternalSubsetHasPEReference || isStandalone) && - DTD.EntityDecls [entityReferenceName] == null) + DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null; + if (this.isStandalone) + if (DTD == null || decl == null || !decl.IsInternalSubset) throw new XmlException (this as IXmlLineInfo, - "Required entity declaration for '" + entityReferenceName + "' was not found."); - string dummy = DTD.EntityDecls [entityReferenceName].EntityValue; - } -*/ + "Standalone document must not contain any references to an non-internally declared entity."); + if (decl != null && decl.NotationName != null) + throw new XmlException (this as IXmlLineInfo, + "Reference to any unparsed entities is not allowed here."); + + ClearValueBuffer (); SetProperties ( XmlNodeType.EntityReference, // nodeType entityReferenceName, // name false, // isEmptyElement - (string) null, // value + null, // value true // clearAttributes ); @@ -1084,11 +1462,14 @@ namespace System.Xml { if (currentState == XmlNodeType.EndElement) throw new XmlException (this as IXmlLineInfo, - "Element cannot appear in this state."); + "Multiple document element was detected."); currentState = XmlNodeType.Element; parserContext.NamespaceManager.PushScope (); + currentLinkedNodeLineNumber = line; + currentLinkedNodeLinePosition = column; + string name = ReadName (); if (currentState == XmlNodeType.EndElement) throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element"); @@ -1107,17 +1488,22 @@ namespace System.Xml attributeTokens [i].FillNames (); // quick name check - for (int i = 0; i < attributeCount; i++) + for (int i = 0; i < attributeCount; i++) { for (int j = i + 1; j < attributeCount; j++) if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) || (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) && Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI))) throw new XmlException (this as IXmlLineInfo, "Attribute name and qualified name must be identical."); + } string baseUri = GetAttribute ("xml:base"); - if (baseUri != null) - parserContext.BaseURI = baseUri; + if (baseUri != null) { + if (this.resolver != null) + parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString (); + else + parserContext.BaseURI = baseUri; + } string xmlLang = GetAttribute ("xml:lang"); if (xmlLang != null) parserContext.XmlLang = xmlLang; @@ -1137,19 +1523,43 @@ namespace System.Xml } else { depthUp = true; - elementStack.Push (name); + PushElementName (name); parserContext.PushScope (); } Expect ('>'); - SetProperties ( XmlNodeType.Element, // nodeType name, // name isEmptyElement, // isEmptyElement - (string) null, // value + null, // value false // clearAttributes ); + + if (LookupNamespace (Prefix) == null) + throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix)); + try { + for (int i = 0; i < attributeCount; i++) { + MoveToAttribute (i); + if (LookupNamespace (Prefix) == null) + throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix)); + } + } finally { + MoveToElement (); + } + + if (IsEmptyElement) + CheckCurrentStateUpdate (); + } + + private void PushElementName (string name) + { + if (elementNames.Length == elementNameStackPos) { + string [] newArray = new string [elementNames.Length * 2]; + Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos); + elementNames = newArray; + } + elementNames [elementNameStackPos++] = name; } // The reader is positioned on the first character @@ -1160,16 +1570,18 @@ namespace System.Xml throw new XmlException (this as IXmlLineInfo, "End tag cannot appear in this state."); + currentLinkedNodeLineNumber = line; + currentLinkedNodeLinePosition = column; + string name = ReadName (); - if (elementStack.Count == 0) + if (elementNameStackPos == 0) throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element"); - string expected = (string)elementStack.Pop(); + string expected = elementNames [--elementNameStackPos]; if (expected != name) throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name)); parserContext.PopScope (); - SkipWhitespace (); - Expect ('>'); + ExpectAfterWhitespace ('>'); --depth; @@ -1177,29 +1589,43 @@ namespace System.Xml XmlNodeType.EndElement, // nodeType name, // name false, // isEmptyElement - (string) null, // value + null, // value true // clearAttributes ); popScope = true; + + CheckCurrentStateUpdate (); } - private void AppendNameChar (int ch) + private void CheckCurrentStateUpdate () { - CheckNameCapacity (); - nameBuffer [nameLength++] = (char)ch; + if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement)) + currentState = XmlNodeType.EndElement; } - private void CheckNameCapacity () + private void AppendNameChar (int ch) { - if (nameLength == nameCapacity) { - nameCapacity = nameCapacity * 2; - char [] oldNameBuffer = nameBuffer; - nameBuffer = new char [nameCapacity]; - Array.Copy (oldNameBuffer, nameBuffer, nameLength); + if (nameLength == nameCapacity) + ExpandNameCapacity (); + if (ch < Char.MaxValue) + nameBuffer [nameLength++] = (char) ch; + else { + nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1); + if (nameLength == nameCapacity) + ExpandNameCapacity (); + nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00); } } + private void ExpandNameCapacity () + { + nameCapacity = nameCapacity * 2; + char [] oldNameBuffer = nameBuffer; + nameBuffer = new char [nameCapacity]; + Array.Copy (oldNameBuffer, nameBuffer, nameLength); + } + private string CreateNameString () { return parserContext.NameTable.Add (nameBuffer, 0, nameLength); @@ -1207,17 +1633,66 @@ namespace System.Xml private void AppendValueChar (int ch) { - valueBuffer.Append ((char)ch); + if (valueLength == valueCapacity) + ExpandValueCapacity (); + if (ch < Char.MaxValue) + valueBuffer [valueLength++] = (char) ch; + else { + valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1); + if (valueLength == valueCapacity) + ExpandValueCapacity (); + valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00); + } + } + + private void ExpandValueCapacity () + { + valueCapacity = valueCapacity * 2; + char [] oldValueBuffer = valueBuffer; + valueBuffer = new char [valueCapacity]; + Array.Copy (oldValueBuffer, valueBuffer, valueLength); } private string CreateValueString () { - return valueBuffer.ToString (); + return new string (valueBuffer, 0, valueLength); } - + private void ClearValueBuffer () { - valueBuffer.Length = 0; + valueLength = 0; + } + + private void AppendCurrentTagChar (int ch) + { + if (currentTagLength == currentTagCapacity) + ExpandCurrentTagCapacity (); + if (ch < Char.MaxValue) + currentTagBuffer [currentTagLength++] = (char) ch; + else { + currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1); + if (currentTagLength == currentTagCapacity) + ExpandCurrentTagCapacity (); + currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00); + } + } + + private void ExpandCurrentTagCapacity () + { + currentTagCapacity = currentTagCapacity * 2; + char [] oldCurrentTagBuffer = currentTagBuffer; + currentTagBuffer = new char [currentTagCapacity]; + Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength); + } + + private string CreateCurrentTagString () + { + return new string (currentTagBuffer, 0, currentTagLength); + } + + private void ClearCurrentTagBuffer () + { + currentTagLength = 0; } // The reader is positioned on the first character @@ -1232,34 +1707,44 @@ namespace System.Xml ClearValueBuffer (); int ch = PeekChar (); - int previousCloseBracketLine = 0; - int previousCloseBracketColumn = 0; + bool previousWasCloseBracket = false; while (ch != '<' && ch != -1) { if (ch == '&') { ReadChar (); - if (ReadReference (false)) + ch = ReadReference (false); + if (returnEntityReference) // Returns -1 if char validation should not be done break; + } else if (normalization && ch == '\r') { + ReadChar (); + ch = ReadChar (); + if (ch != '\n') + // append '\n' instead of '\r'. + AppendValueChar ('\n'); + // and in case of "\r\n", discard '\r'. } else { - if (XmlConstructs.IsInvalid (ch)) - throw new XmlException (this as IXmlLineInfo, - "Not allowed character was found."); - AppendValueChar (ReadChar ()); - if (ch == ']') { - if (previousCloseBracketColumn == LinePosition - 1 && - previousCloseBracketLine == LineNumber) - if (PeekChar () == '>') - throw new XmlException (this as IXmlLineInfo, - "Inside text content, character sequence ']]>' is not allowed."); - previousCloseBracketColumn = LinePosition; - previousCloseBracketLine = LineNumber; - } + if (CharacterChecking && XmlChar.IsInvalid (ch)) + throw new XmlException (this, "Not allowed character was found."); + ch = ReadChar (); + } + + AppendValueChar (ch); + + // Block "]]>" + if (ch == ']') { + if (previousWasCloseBracket) + if (PeekChar () == '>') + throw new XmlException (this as IXmlLineInfo, + "Inside text content, character sequence ']]>' is not allowed."); + previousWasCloseBracket = true; } + else if (previousWasCloseBracket) + previousWasCloseBracket = false; ch = PeekChar (); notWhitespace = true; } - if (returnEntityReference && valueBuffer.Length == 0) { + if (returnEntityReference && valueLength == 0) { SetEntityReferenceProperties (); } else { XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text : @@ -1268,8 +1753,8 @@ namespace System.Xml nodeType, // nodeType String.Empty, // name false, // isEmptyElement - true, // clearAttributes - valueBuffer // value + null, // value: create only when required + true // clearAttributes ); } } @@ -1279,18 +1764,16 @@ namespace System.Xml // character reference or one of the predefined entities. // This allows the ReadText method to break so that the // next call to Read will return the EntityReference node. - private bool ReadReference (bool ignoreEntityReferences) + private int ReadReference (bool ignoreEntityReferences) { if (PeekChar () == '#') { ReadChar (); - ReadCharacterReference (); + return ReadCharacterReference (); } else - ReadEntityReference (ignoreEntityReferences); - - return returnEntityReference; + return ReadEntityReference (ignoreEntityReferences); } - private void ReadCharacterReference () + private int ReadCharacterReference () { int value = 0; @@ -1308,9 +1791,9 @@ namespace System.Xml value = (value << 4) + ch - 'a' + 10; else throw new XmlException (this as IXmlLineInfo, - String.Format ( + String.Format (CultureInfo.InvariantCulture, "invalid hexadecimal digit: {0} (#x{1:X})", - (char)ch, + (char) ch, ch)); } } else { @@ -1321,62 +1804,49 @@ namespace System.Xml value = value * 10 + ch - '0'; else throw new XmlException (this as IXmlLineInfo, - String.Format ( + String.Format (CultureInfo.InvariantCulture, "invalid decimal digit: {0} (#x{1:X})", - (char)ch, + (char) ch, ch)); } } ReadChar (); // ';' - // FIXME: how to handle such chars larger than 0xffff? - if (value < 0xffff && !XmlConstructs.IsValid (value)) + // There is no way to save surrogate pairs... + if (CharacterChecking && XmlChar.IsInvalid (value)) throw new XmlException (this as IXmlLineInfo, - "Referenced character was not allowed in XML."); - AppendValueChar (value); + "Referenced character was not allowed in XML. Normalization is " + normalization + ", checkCharacters = " + checkCharacters); + return value; } - private void ReadEntityReference (bool ignoreEntityReferences) + // Returns -1 if it should not be validated. + // Real EOF must not be detected here. + private int ReadEntityReference (bool ignoreEntityReferences) { - nameLength = 0; - - int ch = PeekChar (); - - while (ch != ';' && ch != -1) { - AppendNameChar (ReadChar ()); - ch = PeekChar (); - } - + string name = ReadName (); Expect (';'); - string name = CreateNameString (); - if (!XmlChar.IsName (name)) - throw new XmlException (this as IXmlLineInfo, - "Invalid entity reference name was found."); - - char predefined = XmlChar.GetPredefinedEntity (name); - if (predefined != 0) - AppendValueChar (predefined); + int predefined = XmlChar.GetPredefinedEntity (name); + if (predefined >= 0) + return predefined; else { if (ignoreEntityReferences) { AppendValueChar ('&'); - - foreach (char ch2 in name) { - AppendValueChar (ch2); - } - + for (int i = 0; i < name.Length; i++) + AppendValueChar (name [i]); AppendValueChar (';'); } else { returnEntityReference = true; entityReferenceName = name; } } + return -1; } // The reader is positioned on the first character of // the attribute name. - private void ReadAttributes (bool endsWithQuestion) + private void ReadAttributes (bool isXmlDecl) { int peekChar = -1; bool requireWhitespace = false; @@ -1388,12 +1858,12 @@ namespace System.Xml throw new XmlException ("Unexpected token. Name is required here."); IncrementAttributeToken (); - currentAttributeToken.LineNumber = currentInput.LineNumber; - currentAttributeToken.LinePosition = currentInput.LinePosition; + currentAttributeToken.LineNumber = line; + currentAttributeToken.LinePosition = column; - currentAttributeToken.Name = ReadName (); - SkipWhitespace (); - Expect ('='); + currentAttributeToken.LocalName = + currentAttributeToken.Name = ReadName (); + ExpectAfterWhitespace ('='); SkipWhitespace (); ReadAttributeValueTokens (-1); attributeCount++; @@ -1401,14 +1871,14 @@ namespace System.Xml if (currentAttributeToken.Name == "xmlns") parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute)); else if (currentAttributeToken.Name.StartsWith ("xmlns:")) { - string nsPrefix = NameTable.Add (currentAttributeToken.Name.Substring (6)); + string nsPrefix = currentAttributeToken.Name.Substring (6); parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute)); } if (!SkipWhitespace ()) requireWhitespace = true; peekChar = PeekChar (); - if (endsWithQuestion) { + if (isXmlDecl) { if (peekChar == '?') break; } @@ -1429,7 +1899,7 @@ namespace System.Xml IncrementAttributeValueToken (); XmlTokenInfo vti = attributeValueTokens [currentAttributeValue]; vti.Value = value; - SetProperties (vti, XmlNodeType.Text, name, false, value, false); + SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false); attributeCount++; } @@ -1458,11 +1928,12 @@ namespace System.Xml attributeValueTokens = newArray; } if (attributeValueTokens [currentAttributeValue] == null) - attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this); + attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false); currentAttributeValueToken = attributeValueTokens [currentAttributeValue]; currentAttributeValueToken.Clear (); } + // LAMESPEC: Orthodox XML reader should normalize attribute values private void ReadAttributeValueTokens (int dummyQuoteChar) { int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar; @@ -1473,23 +1944,26 @@ namespace System.Xml IncrementAttributeValueToken (); currentAttributeToken.ValueTokenStartIndex = currentAttributeValue; - currentAttributeValueToken.LineNumber = currentInput.LineNumber; - currentAttributeValueToken.LinePosition = currentInput.LinePosition; + currentAttributeValueToken.LineNumber = line; + currentAttributeValueToken.LinePosition = column; bool incrementToken = false; bool isNewToken = true; bool loop = true; - while (loop && PeekChar () != quoteChar) { + int ch = 0; + while (loop) { + ch = ReadChar (); + if (ch == quoteChar) + break; + if (incrementToken) { IncrementAttributeValueToken (); - currentAttributeValueToken.LineNumber = currentInput.LineNumber; - currentAttributeValueToken.LinePosition = currentInput.LinePosition; + currentAttributeValueToken.LineNumber = line; + currentAttributeValueToken.LinePosition = column; incrementToken = false; isNewToken = true; } - int ch = ReadChar (); - switch (ch) { case '<': @@ -1501,23 +1975,22 @@ namespace System.Xml loop = false; break; case '&': - int startPosition = currentTag.Length - 1; + int startPosition = currentTagLength - 1; if (PeekChar () == '#') { ReadChar (); - this.ReadCharacterReference (); + ch = ReadCharacterReference (); + if (CharacterChecking && XmlChar.IsInvalid (ch)) + throw new XmlException (this as IXmlLineInfo, + "Not allowed character was found."); + AppendValueChar (ch); break; } // Check XML 1.0 section 3.1 WFC. string entName = ReadName (); Expect (';'); int predefined = XmlChar.GetPredefinedEntity (entName); - if (predefined == 0) { - DTDEntityDeclaration entDecl = - DTD == null ? null : DTD.EntityDecls [entName]; - if (entDecl != null && entDecl.SystemId != null) -// if (!startNodeType == XmlNodeType.Attribute && (entDecl == null || entDecl.SystemId != null)) - throw new XmlException (this as IXmlLineInfo, - "Reference to external entities is not allowed in the value of an attribute."); + if (predefined < 0) { + CheckAttributeEntityReferenceWFC (entName); currentAttributeValueToken.Value = CreateValueString (); currentAttributeValueToken.NodeType = XmlNodeType.Text; if (!isNewToken) @@ -1531,6 +2004,8 @@ namespace System.Xml AppendValueChar (predefined); break; default: + if (CharacterChecking && XmlChar.IsInvalid (ch)) + throw new XmlException (this, "Invalid character was found."); AppendValueChar (ch); break; } @@ -1540,69 +2015,27 @@ namespace System.Xml if (!incrementToken) { currentAttributeValueToken.Value = CreateValueString (); currentAttributeValueToken.NodeType = XmlNodeType.Text; - currentAttributeToken.ValueTokenEndIndex = currentAttributeValue; } + currentAttributeToken.ValueTokenEndIndex = currentAttributeValue; - if (dummyQuoteChar < 0) - ReadChar (); // quoteChar } - // The reader is positioned on the quote character. - // *Keeps quote char* to value to get_QuoteChar() correctly. - // Not it is used only for DTD. - private string ReadAttribute (bool isDefaultValue) + private void CheckAttributeEntityReferenceWFC (string entName) { - ClearValueBuffer (); - - int quoteChar = ReadChar (); - - if (quoteChar != '\'' && quoteChar != '\"') - throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted"); - - AppendValueChar (quoteChar); - - while (PeekChar () != quoteChar) { - int ch = ReadChar (); - - switch (ch) - { - case '<': - throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'"); - case -1: - throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value"); -/* - case '&': - if (isDefaultValue) { - AppendValueChar (ch); - break; - } - AppendValueChar (ch); - if (PeekChar () == '#') - break; - // Check XML 1.0 section 3.1 WFC. - string entName = ReadName (); - Expect (';'); - if (XmlChar.GetPredefinedEntity (entName) == 0) { - DTDEntityDeclaration entDecl = - DTD == null ? null : DTD.EntityDecls [entName]; - if (entDecl == null || entDecl.SystemId != null) - throw new XmlException (this as IXmlLineInfo, - "Reference to external entities is not allowed in attribute value."); - } - valueBuffer.Append (entName); - AppendValueChar (';'); - break; -*/ - default: - AppendValueChar (ch); - break; - } - } + DTDEntityDeclaration entDecl = + DTD == null ? null : DTD.EntityDecls [entName]; + if (DTD != null && resolver != null && entDecl == null) + throw new XmlException (this, "Referenced entity does not exist."); - ReadChar (); // quoteChar - AppendValueChar (quoteChar); + if (entDecl == null) + return; - return CreateValueString (); + if (entDecl.HasExternalReference) + throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute."); + if (isStandalone && !entDecl.IsInternalSubset) + throw new XmlException (this, "Reference to external entities is not allowed in the internal subset."); + if (entDecl.EntityValue.IndexOf ('<') >= 0) + throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references."); } // The reader is positioned on the first character @@ -1615,7 +2048,7 @@ namespace System.Xml if (target == "xml") { ReadXmlDeclaration (); return; - } else if (target.ToLower () == "xml") + } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml") throw new XmlException (this as IXmlLineInfo, "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found."); @@ -1637,15 +2070,17 @@ namespace System.Xml break; } - AppendValueChar ((char)ch); + if (CharacterChecking && XmlChar.IsInvalid (ch)) + throw new XmlException (this, "Invalid character was found."); + AppendValueChar (ch); } SetProperties ( XmlNodeType.ProcessingInstruction, // nodeType target, // name false, // isEmptyElement - true, // clearAttributes - valueBuffer // value + null, // value: create only when required + true // clearAttributes ); } @@ -1653,13 +2088,10 @@ namespace System.Xml private void ReadXmlDeclaration () { if (currentState != XmlNodeType.None) { - if (maybeTextDecl == 0) - throw new XmlException (this as IXmlLineInfo, - "XML declaration cannot appear in this state."); + throw new XmlException (this as IXmlLineInfo, + "XML declaration cannot appear in this state."); } - // Is this required? - if (maybeTextDecl != 0) - currentState = XmlNodeType.XmlDeclaration; + currentState = XmlNodeType.XmlDeclaration; ClearAttributes (); @@ -1667,113 +2099,192 @@ namespace System.Xml string version = GetAttribute ("version"); string message = null; - if (parserInputStack.Count == 0) { - if (maybeTextDecl == 0 && (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 = "Only 'yes' or 'no' is allowed for standalone."; - - this.isStandalone = (sa == "yes"); - } else { - int currentCheck = 0; - if (attributeTokens [0].Name == "version") { - if (version != "1.0") - message = "Version 1.0 declaration is required in Text Declaration."; - currentCheck = 1; - } - if (attributeCount <= currentCheck || attributeTokens [currentCheck].Name != "encoding") - message = "Invalid Text Declaration markup was found. encoding specification is required."; - } - if (message != null) - throw new XmlException (this as IXmlLineInfo, message); - Expect ("?>"); + 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 = "Only 'yes' or 'no' is allowed for standalone."; - if (maybeTextDecl != 0) - if (this ["standalone"] != null) - throw new XmlException (this as IXmlLineInfo, - "Invalid text declaration."); - if (maybeTextDecl == 2) - maybeTextDecl = 1; + this.isStandalone = (sa == "yes"); + + if (message != null) + throw new XmlException (this as IXmlLineInfo, message); SetProperties ( XmlNodeType.XmlDeclaration, // nodeType "xml", // name false, // isEmptyElement - currentInput.CurrentMarkup.ToString (6, currentInput.CurrentMarkup.Length - 6), // value + new string (currentTagBuffer, 6, currentTagLength - 6), // value false // clearAttributes ); - } - // The reader is positioned on the first character after - // the leading '"); } - // The reader is positioned on the first character after - // the leading ' Value: "&ent; &ent;".) - resolved.Append ("&" + entityName + ";"); + ch = ReadChar (); + switch (ch) { + case -1: + throw new XmlException (this as IXmlLineInfo, + "Unexpected end of xml."); + case '<': + if (PeekChar () != '/') + continue; + ReadChar (); + string name = ReadName (); + if (name != elementNames [elementNameStackPos - 1]) + continue; + Expect ('>'); + depth--; + elementNames [--elementNameStackPos] = null; + return Read (); } - pos = endPos + 1; - if(pos > unresolved.Length) - break; - next = unresolved.IndexOf('&', pos); - } - resolved.Append (unresolved.Substring(pos)); - - return resolved.ToString(); + } while (true); } - #endregion } }