2 // System.Xml.XmlTextReader
5 // Jason Diamond (jason@injektilo.org)
6 // Adam Treat (manyoso@yahoo.com)
7 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 // (C) 2001, 2002 Jason Diamond http://injektilo.org/
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
35 using System.Collections.Generic;
37 using System.Globalization;
39 using System.Security.Permissions;
41 using System.Xml.Schema;
54 internal class XmlTextReader : XmlReader,
55 IXmlLineInfo, IXmlNamespaceResolver, IHasXmlParserContext
57 [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
58 public class XmlTextReader : XmlReader, IXmlLineInfo, IHasXmlParserContext
63 protected XmlTextReader ()
67 public XmlTextReader (Stream input)
68 : this (new XmlStreamReader (input))
72 public XmlTextReader (string url)
73 : this(url, new NameTable ())
77 public XmlTextReader (TextReader input)
78 : this (input, new NameTable ())
82 protected XmlTextReader (XmlNameTable nt)
83 : this (String.Empty, null, XmlNodeType.None, null)
87 public XmlTextReader (Stream input, XmlNameTable nt)
88 : this(new XmlStreamReader (input), nt)
92 public XmlTextReader (string url, Stream input)
93 : this (url, new XmlStreamReader (input))
97 public XmlTextReader (string url, TextReader input)
98 : this (url, input, new NameTable ())
102 public XmlTextReader (string url, XmlNameTable nt)
105 Stream stream = GetStreamFromUrl (url, out uriString);
106 XmlParserContext ctx = new XmlParserContext (nt,
107 new XmlNamespaceManager (nt),
110 this.InitializeContext (uriString, ctx, new XmlStreamReader (stream), XmlNodeType.Document);
113 public XmlTextReader (TextReader input, XmlNameTable nt)
114 : this (String.Empty, input, nt)
118 // This is used in XmlReader.Create() to indicate that string
119 // argument is uri, not an xml fragment.
120 internal XmlTextReader (bool dummy, string url, XmlNodeType fragType, XmlParserContext context)
123 Stream stream = GetStreamFromUrl (url, out uriString);
124 this.InitializeContext (uriString, context, new XmlStreamReader (stream), fragType);
127 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
128 : this (context != null ? context.BaseURI : String.Empty,
129 new XmlStreamReader (xmlFragment),
135 internal XmlTextReader (string baseURI, TextReader xmlFragment, XmlNodeType fragType)
136 : this (baseURI, xmlFragment, fragType, null)
140 public XmlTextReader (string url, Stream input, XmlNameTable nt)
141 : this (url, new XmlStreamReader (input), nt)
145 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
146 : this (url, input, XmlNodeType.Document, null)
150 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
151 : this (context != null ? context.BaseURI : String.Empty,
152 new StringReader (xmlFragment),
158 internal XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
160 InitializeContext (url, context, fragment, fragType);
163 private Stream GetStreamFromUrl (string url, out string absoluteUriString)
165 Uri uri = resolver.ResolveUri (null, url);
166 absoluteUriString = uri != null ? uri.ToString () : String.Empty;
167 return resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
174 public override int AttributeCount
176 get { return attributeCount; }
179 public override string BaseURI
181 get { return parserContext.BaseURI; }
185 public override bool CanReadBinaryContent {
189 public override bool CanReadValueChunk {
193 internal override bool CanReadBinaryContent {
197 internal override bool CanReadValueChunk {
202 internal bool CharacterChecking {
203 get { return checkCharacters; }
204 set { checkCharacters = value; }
207 // for XmlReaderSettings.CloseInput support
208 internal bool CloseInput {
209 get { return closeInput; }
210 set { closeInput = value; }
213 public override int Depth
216 int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element ? 0 : -1;
217 if (currentAttributeValue >= 0)
218 return nodeTypeMod + elementDepth + 2; // inside attribute value.
219 else if (currentAttribute >= 0)
220 return nodeTypeMod + elementDepth + 1;
225 public Encoding Encoding
227 get { return parserContext.Encoding; }
230 public EntityHandling EntityHandling {
231 get { return entityHandling; }
232 set { entityHandling = value; }
236 public override bool EOF {
237 get { return readState == ReadState.EndOfFile; }
240 public override bool HasValue {
241 get { return cursorToken.Value != null; }
244 public override bool IsDefault {
245 // XmlTextReader does not expand default attributes.
246 get { return false; }
249 public override bool IsEmptyElement {
250 get { return cursorToken.IsEmptyElement; }
255 public override string this [int i] {
256 get { return GetAttribute (i); }
259 public override string this [string name] {
260 get { return GetAttribute (name); }
263 public override string this [string localName, string namespaceName] {
264 get { return GetAttribute (localName, namespaceName); }
268 public int LineNumber {
270 if (useProceedingLineInfo)
273 return cursorToken.LineNumber;
277 public int LinePosition {
279 if (useProceedingLineInfo)
282 return cursorToken.LinePosition;
286 public override string LocalName {
287 get { return cursorToken.LocalName; }
290 public override string Name {
291 get { return cursorToken.Name; }
294 public bool Namespaces {
295 get { return namespaces; }
297 if (readState != ReadState.Initial)
298 throw new InvalidOperationException ("Namespaces have to be set before reading.");
303 public override string NamespaceURI {
304 get { return cursorToken.NamespaceURI; }
307 public override XmlNameTable NameTable {
308 get { return parserContext.NameTable; }
311 public override XmlNodeType NodeType {
312 get { return cursorToken.NodeType; }
315 public bool Normalization {
316 get { return normalization; }
317 set { normalization = value; }
320 public override string Prefix {
321 get { return cursorToken.Prefix; }
325 public bool ProhibitDtd {
326 get { return prohibitDtd; }
327 set { prohibitDtd = value; }
331 public override char QuoteChar {
332 get { return cursorToken.QuoteChar; }
335 public override ReadState ReadState {
336 get { return readState; }
340 public override XmlReaderSettings Settings {
341 get { return base.Settings; }
345 public override string Value {
346 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
349 public WhitespaceHandling WhitespaceHandling {
350 get { return whitespaceHandling; }
351 set { whitespaceHandling = value; }
354 public override string XmlLang {
355 get { return parserContext.XmlLang; }
358 public XmlResolver XmlResolver {
359 set { resolver = value; }
362 public override XmlSpace XmlSpace {
363 get { return parserContext.XmlSpace; }
370 public override void Close ()
372 readState = ReadState.Closed;
374 cursorToken.Clear ();
375 currentToken.Clear ();
377 if (closeInput && reader != null)
381 public override string GetAttribute (int i)
383 if (i >= attributeCount)
384 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
386 return attributeTokens [i].Value;
390 // MS.NET 1.0 msdn says that this method returns String.Empty
391 // for absent attribute, but in fact it returns null.
392 // This description is corrected in MS.NET 1.1 msdn.
393 public override string GetAttribute (string name)
395 for (int i = 0; i < attributeCount; i++)
396 if (attributeTokens [i].Name == name)
397 return attributeTokens [i].Value;
401 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
403 for (int i = 0; i < attributeCount; i++) {
404 XmlAttributeTokenInfo ti = attributeTokens [i];
405 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
411 XmlParserContext IHasXmlParserContext.ParserContext {
412 get { return parserContext; }
415 public override string GetAttribute (string localName, string namespaceURI)
417 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
420 return attributeTokens [idx].Value;
424 public IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
426 return parserContext.NamespaceManager.GetNamespacesInScope (scope);
429 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
431 return GetNamespacesInScope (scope);
435 public TextReader GetRemainder ()
437 if (peekCharsIndex == peekCharsLength)
439 return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
443 public bool HasLineInfo ()
445 bool IXmlLineInfo.HasLineInfo ()
451 public override string LookupNamespace (string prefix)
453 return LookupNamespace (prefix, false);
456 private string LookupNamespace (string prefix, bool atomizedNames)
458 string s = parserContext.NamespaceManager.LookupNamespace (
459 prefix, atomizedNames);
460 return s == String.Empty ? null : s;
464 string IXmlNamespaceResolver.LookupPrefix (string ns)
466 return LookupPrefix (ns, false);
469 public string LookupPrefix (string ns, bool atomizedName)
471 return parserContext.NamespaceManager.LookupPrefix (ns, atomizedName);
475 public override void MoveToAttribute (int i)
477 if (i >= attributeCount)
478 throw new ArgumentOutOfRangeException ("attribute index out of range.");
480 currentAttribute = i;
481 currentAttributeValue = -1;
482 cursorToken = attributeTokens [i];
485 public override bool MoveToAttribute (string name)
487 for (int i = 0; i < attributeCount; i++) {
488 XmlAttributeTokenInfo ti = attributeTokens [i];
489 if (ti.Name == name) {
497 public override bool MoveToAttribute (string localName, string namespaceName)
499 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
502 MoveToAttribute (idx);
506 public override bool MoveToElement ()
508 if (currentToken == null) // for attribute .ctor()
511 if (cursorToken == currentToken)
514 if (currentAttribute >= 0) {
515 currentAttribute = -1;
516 currentAttributeValue = -1;
517 cursorToken = currentToken;
524 public override bool MoveToFirstAttribute ()
526 if (attributeCount == 0)
529 return MoveToNextAttribute ();
532 public override bool MoveToNextAttribute ()
534 if (currentAttribute == 0 && attributeCount == 0)
536 if (currentAttribute + 1 < attributeCount) {
538 currentAttributeValue = -1;
539 cursorToken = attributeTokens [currentAttribute];
546 public override bool Read ()
548 if (startNodeType == XmlNodeType.Attribute) {
549 if (currentAttribute == 0)
550 return false; // already read.
552 IncrementAttributeToken ();
553 ReadAttributeValueTokens ('"');
554 cursorToken = attributeTokens [0];
555 currentAttributeValue = -1;
556 readState = ReadState.Interactive;
564 readState = ReadState.Interactive;
565 currentLinkedNodeLineNumber = line;
566 currentLinkedNodeLinePosition = column;
567 useProceedingLineInfo = true;
569 cursorToken = currentToken;
571 currentAttribute = currentAttributeValue = -1;
572 currentToken.Clear ();
574 // It was moved from end of ReadStartTag ().
580 if (shouldSkipUntilEndTag) {
581 shouldSkipUntilEndTag = false;
582 return ReadUntilEndTag ();
585 more = ReadContent ();
587 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
588 throw NotWFError ("Document element did not appear.");
590 useProceedingLineInfo = false;
594 public override bool ReadAttributeValue ()
596 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
600 if (currentAttribute < 0)
602 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
603 if (currentAttributeValue < 0)
604 currentAttributeValue = ti.ValueTokenStartIndex - 1;
606 if (currentAttributeValue < ti.ValueTokenEndIndex) {
607 currentAttributeValue++;
608 cursorToken = attributeValueTokens [currentAttributeValue];
615 public int ReadBase64 (byte [] buffer, int offset, int length)
617 BinaryCharGetter = binaryCharGetter;
619 return Binary.ReadBase64 (buffer, offset, length);
621 BinaryCharGetter = null;
625 public int ReadBinHex (byte [] buffer, int offset, int length)
627 BinaryCharGetter = binaryCharGetter;
629 return Binary.ReadBinHex (buffer, offset, length);
631 BinaryCharGetter = null;
635 public int ReadChars (char [] buffer, int offset, int length)
638 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
640 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
641 else if (buffer.Length < offset + length)
642 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
644 if (IsEmptyElement) {
649 if (NodeType != XmlNodeType.Element)
652 return ReadCharsInternal (buffer, offset, length);
656 public override string ReadInnerXml ()
658 return ReadInnerXmlInternal ();
661 public override string ReadOuterXml ()
663 return ReadOuterXmlInternal ();
666 public override string ReadString ()
668 return ReadStringInternal ();
672 public void ResetState ()
674 throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment.");
678 public override void ResolveEntity ()
680 // XmlTextReader does not resolve entities.
681 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
685 [MonoTODO ("Implement for performance reason")]
686 public override void Skip ()
694 // Parsed DTD Objects
695 internal DTDObjectModel DTD {
696 get { return parserContext.Dtd; }
699 internal XmlResolver Resolver {
700 get { return resolver; }
705 internal class XmlTokenInfo
707 public XmlTokenInfo (XmlTextReader xtr)
715 protected XmlTextReader Reader;
718 public string LocalName;
719 public string Prefix;
720 public string NamespaceURI;
721 public bool IsEmptyElement;
722 public char QuoteChar;
723 public int LineNumber;
724 public int LinePosition;
725 public int ValueBufferStart;
726 public int ValueBufferEnd;
728 public XmlNodeType NodeType;
730 public virtual string Value {
732 if (valueCache != null)
734 if (ValueBufferStart >= 0) {
735 //Console.WriteLine (NodeType + " / " + ValueBuffer.Length + " / " + ValueBufferStart + " / " + ValueBufferEnd);
736 valueCache = Reader.valueBuffer.ToString (ValueBufferStart, ValueBufferEnd - ValueBufferStart);
740 case XmlNodeType.Text:
741 case XmlNodeType.SignificantWhitespace:
742 case XmlNodeType.Whitespace:
743 case XmlNodeType.Comment:
744 case XmlNodeType.CDATA:
745 case XmlNodeType.ProcessingInstruction:
746 valueCache = Reader.CreateValueString ();
751 set { valueCache = value; }
754 public virtual void Clear ()
756 ValueBufferStart = -1;
758 NodeType = XmlNodeType.None;
759 Name = LocalName = Prefix = NamespaceURI = String.Empty;
760 IsEmptyElement = false;
762 LineNumber = LinePosition = 0;
766 internal class XmlAttributeTokenInfo : XmlTokenInfo
768 public XmlAttributeTokenInfo (XmlTextReader reader)
771 NodeType = XmlNodeType.Attribute;
774 public int ValueTokenStartIndex;
775 public int ValueTokenEndIndex;
777 StringBuilder tmpBuilder = new StringBuilder ();
779 public override string Value {
781 if (valueCache != null)
784 // An empty value should return String.Empty.
785 if (ValueTokenStartIndex == ValueTokenEndIndex) {
786 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
787 if (ti.NodeType == XmlNodeType.EntityReference)
788 valueCache = String.Concat ("&", ti.Name, ";");
790 valueCache = ti.Value;
794 tmpBuilder.Length = 0;
795 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
796 XmlTokenInfo ti = Reader.attributeValueTokens [i];
797 if (ti.NodeType == XmlNodeType.Text)
798 tmpBuilder.Append (ti.Value);
800 tmpBuilder.Append ('&');
801 tmpBuilder.Append (ti.Name);
802 tmpBuilder.Append (';');
806 valueCache = tmpBuilder.ToString (0, tmpBuilder.Length);
810 set { valueCache = value; }
813 public override void Clear ()
817 NodeType = XmlNodeType.Attribute;
818 ValueTokenStartIndex = ValueTokenEndIndex = 0;
821 internal void FillXmlns ()
823 if (Object.ReferenceEquals (Prefix, XmlNamespaceManager.PrefixXmlns))
824 Reader.parserContext.NamespaceManager.AddNamespace (LocalName, Value);
825 else if (Object.ReferenceEquals (Name, XmlNamespaceManager.PrefixXmlns))
826 Reader.parserContext.NamespaceManager.AddNamespace (String.Empty, Value);
829 internal void FillNamespace ()
831 if (Object.ReferenceEquals (Prefix, XmlNamespaceManager.PrefixXmlns) ||
832 Object.ReferenceEquals (Name, XmlNamespaceManager.PrefixXmlns))
833 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
834 else if (Prefix.Length == 0)
835 NamespaceURI = string.Empty;
837 NamespaceURI = Reader.LookupNamespace (Prefix, true);
841 private XmlTokenInfo cursorToken;
842 private XmlTokenInfo currentToken;
843 private XmlAttributeTokenInfo currentAttributeToken;
844 private XmlTokenInfo currentAttributeValueToken;
845 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
846 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
847 private int currentAttribute;
848 private int currentAttributeValue;
849 private int attributeCount;
851 private XmlParserContext parserContext;
853 private ReadState readState;
856 private int elementDepth;
857 private bool depthUp;
859 private bool popScope;
861 private string [] elementNames;
862 int elementNameStackPos;
864 private bool allowMultipleRoot;
866 private bool isStandalone;
868 private bool returnEntityReference;
869 private string entityReferenceName;
871 private char [] nameBuffer;
872 private int nameLength;
873 private int nameCapacity;
874 private const int initialNameCapacity = 32;
876 private StringBuilder valueBuffer;
878 private char [] currentTagBuffer;
879 private int currentTagLength;
880 private int currentTagCapacity;
881 private const int initialCurrentTagCapacity = 256;
883 private TextReader reader;
884 private char [] peekChars;
885 private int peekCharsIndex;
886 private int peekCharsLength;
887 private const int peekCharCapacity = 1024;
892 private int currentLinkedNodeLineNumber;
893 private int currentLinkedNodeLinePosition;
894 private bool useProceedingLineInfo;
896 private XmlNodeType startNodeType;
897 // State machine attribute.
898 // XmlDeclaration: after the first node.
899 // DocumentType: after doctypedecl
900 // Element: inside document element
901 // EndElement: after document element
902 private XmlNodeType currentState;
904 // For ReadChars()/ReadBase64()/ReadBinHex()
905 private bool shouldSkipUntilEndTag;
906 XmlReaderBinarySupport.CharGetter binaryCharGetter;
908 // These values are never re-initialized.
909 private bool namespaces = true;
910 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
911 private XmlResolver resolver = new XmlUrlResolver ();
912 private bool normalization = false;
914 private bool checkCharacters;
915 private bool prohibitDtd = false;
916 private bool closeInput = true;
917 private EntityHandling entityHandling; // 2.0
919 private XmlException NotWFError (string message)
921 return new XmlException (this as IXmlLineInfo, BaseURI, message);
926 currentToken = new XmlTokenInfo (this);
927 cursorToken = currentToken;
928 currentAttribute = -1;
929 currentAttributeValue = -1;
932 readState = ReadState.Initial;
933 allowMultipleRoot = false;
939 popScope = allowMultipleRoot = false;
940 elementNames = new string [10];
941 elementNameStackPos = 0;
943 isStandalone = false;
944 returnEntityReference = false;
945 entityReferenceName = String.Empty;
947 nameBuffer = new char [initialNameCapacity];
949 nameCapacity = initialNameCapacity;
951 valueBuffer = new StringBuilder ();
953 currentTagBuffer = new char [initialCurrentTagCapacity];
954 currentTagLength = 0;
955 currentTagCapacity = initialCurrentTagCapacity;
959 if (peekChars == null)
960 peekChars = new char [peekCharCapacity];
965 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
966 useProceedingLineInfo = false;
968 currentState = XmlNodeType.None;
970 shouldSkipUntilEndTag = false;
971 binaryCharGetter = new XmlReaderBinarySupport.CharGetter (ReadChars);
973 checkCharacters = true;
975 if (Settings != null)
976 checkCharacters = Settings.CheckCharacters;
980 entityHandling = EntityHandling.ExpandCharEntities;
983 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
985 startNodeType = fragType;
986 parserContext = context;
987 if (context == null) {
988 XmlNameTable nt = new NameTable ();
989 parserContext = new XmlParserContext (nt,
990 new XmlNamespaceManager (nt),
995 if (url != null && url.Length > 0) {
999 } catch (Exception) {
1000 string path = Path.GetFullPath ("./a");
1001 uri = new Uri (new Uri (path), url);
1003 parserContext.BaseURI = uri.ToString ();
1011 case XmlNodeType.Attribute:
1012 reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1013 SkipTextDeclaration ();
1015 case XmlNodeType.Element:
1016 currentState = XmlNodeType.Element;
1017 allowMultipleRoot = true;
1018 SkipTextDeclaration ();
1020 case XmlNodeType.Document:
1023 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1028 internal ConformanceLevel Conformance {
1029 get { return allowMultipleRoot ? ConformanceLevel.Fragment : ConformanceLevel.Document; }
1031 if (value == ConformanceLevel.Fragment) {
1032 currentState = XmlNodeType.Element;
1033 allowMultipleRoot = true;
1038 internal void AdjustLineInfoOffset (int lineNumberOffset, int linePositionOffset)
1040 line += lineNumberOffset;
1041 column += linePositionOffset;
1044 internal void SetNameTable (XmlNameTable nameTable)
1046 parserContext.NameTable = nameTable;
1050 // Use this method rather than setting the properties
1051 // directly so that all the necessary properties can
1052 // be changed in harmony with each other. Maybe the
1053 // fields should be in a seperate class to help enforce
1056 // Namespace URI could not be provided here.
1057 private void SetProperties (
1058 XmlNodeType nodeType,
1062 bool isEmptyElement,
1064 bool clearAttributes)
1066 SetTokenProperties (currentToken, nodeType, name, prefix, localName, isEmptyElement, value, clearAttributes);
1067 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1068 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1071 private void SetTokenProperties (
1073 XmlNodeType nodeType,
1077 bool isEmptyElement,
1079 bool clearAttributes)
1081 token.NodeType = nodeType;
1083 token.Prefix = prefix;
1084 token.LocalName = localName;
1085 token.IsEmptyElement = isEmptyElement;
1086 token.Value = value;
1087 this.elementDepth = depth;
1089 if (clearAttributes)
1093 private void ClearAttributes ()
1095 for (int i = 0; i < attributeCount; i++)
1096 attributeTokens [i].Clear ();
1098 currentAttribute = -1;
1099 currentAttributeValue = -1;
1102 private int PeekChar ()
1104 if (peekCharsLength == peekCharsIndex) {
1105 if (!ReadTextReader (-1))
1110 char c = peekChars [peekCharsIndex];
1113 if (c < 0xD800 || 0xDFFF < c)
1115 if (peekCharsLength == peekCharsIndex + 1) {
1116 if (!ReadTextReader (c))
1117 //FIXME: copy MS.NET behaviour when unpaired surrogate found
1121 char highhalfChar = peekChars [peekCharsIndex];
1122 char lowhalfChar = peekChars [peekCharsIndex+1];
1124 if (((highhalfChar & 0xFC00) != 0xD800) || ((lowhalfChar & 0xFC00) != 0xDC00))
1125 //FIXME: copy MS.NET behaviour when unpaired surrogate found
1126 return highhalfChar;
1127 return 0x10000 + (highhalfChar-0xD800)*0x400 + (lowhalfChar-0xDC00);
1130 private int ReadChar ()
1132 int ch = PeekChar ();
1137 peekCharsIndex++; //Increment by 2 when a compound UCS-4 character was found
1142 } else if (ch == -1) {
1147 if (currentState != XmlNodeType.Element)
1148 AppendCurrentTagChar (ch);
1152 private bool ReadTextReader (int remained)
1156 peekChars [0] = (char) remained;
1157 int offset = remained >= 0 ? 1 : 0;
1158 peekCharsLength = reader.Read (peekChars, offset,
1159 peekCharCapacity - offset) + offset;
1160 return (peekCharsLength != 0);
1163 private bool ReadContent ()
1165 currentTagLength = 0;
1167 parserContext.NamespaceManager.PopScope ();
1168 parserContext.PopScope ();
1172 if (returnEntityReference)
1173 SetEntityReferenceProperties ();
1175 int c = PeekChar ();
1177 readState = ReadState.EndOfFile;
1178 ClearValueBuffer ();
1180 XmlNodeType.None, // nodeType
1181 String.Empty, // name
1182 String.Empty, // prefix
1183 String.Empty, // localName
1184 false, // isEmptyElement
1186 true // clearAttributes
1189 throw NotWFError ("unexpected end of file. Current depth is " + depth);
1196 switch (PeekChar ())
1204 ReadProcessingInstruction ();
1219 if (whitespaceHandling == WhitespaceHandling.All ||
1220 whitespaceHandling == WhitespaceHandling.Significant)
1224 return ReadContent ();
1233 return this.ReadState != ReadState.EndOfFile;
1236 private void SetEntityReferenceProperties ()
1238 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1239 if (this.isStandalone)
1240 if (DTD == null || decl == null || !decl.IsInternalSubset)
1241 throw NotWFError ("Standalone document must not contain any references to an non-internally declared entity.");
1242 if (decl != null && decl.NotationName != null)
1243 throw NotWFError ("Reference to any unparsed entities is not allowed here.");
1245 ClearValueBuffer ();
1247 XmlNodeType.EntityReference, // nodeType
1248 entityReferenceName, // name
1249 String.Empty, // prefix
1250 entityReferenceName, // localName
1251 false, // isEmptyElement
1253 true // clearAttributes
1256 returnEntityReference = false;
1257 entityReferenceName = String.Empty;
1260 // The leading '<' has already been consumed.
1261 private void ReadStartTag ()
1263 if (currentState == XmlNodeType.EndElement)
1264 throw NotWFError ("Multiple document element was detected.");
1265 currentState = XmlNodeType.Element;
1267 parserContext.NamespaceManager.PushScope ();
1269 currentLinkedNodeLineNumber = line;
1270 currentLinkedNodeLinePosition = column;
1272 string prefix, localName;
1273 string name = ReadName (out prefix, out localName);
1274 if (currentState == XmlNodeType.EndElement)
1275 throw NotWFError ("document has terminated, cannot open new element");
1277 bool isEmptyElement = false;
1282 if (XmlChar.IsFirstNameChar (PeekChar ()))
1283 ReadAttributes (false);
1284 cursorToken = this.currentToken;
1287 for (int i = 0; i < attributeCount; i++)
1288 attributeTokens [i].FillXmlns ();
1289 for (int i = 0; i < attributeCount; i++)
1290 attributeTokens [i].FillNamespace ();
1294 for (int i = 0; i < attributeCount; i++)
1295 if (attributeTokens [i].Prefix == "xmlns" &&
1296 attributeTokens [i].Value == String.Empty)
1297 throw NotWFError ("Empty namespace URI cannot be mapped to non-empty prefix.");
1299 for (int i = 0; i < attributeCount; i++) {
1300 for (int j = i + 1; j < attributeCount; j++)
1301 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1302 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1303 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1304 throw NotWFError ("Attribute name and qualified name must be identical.");
1307 if (PeekChar () == '/') {
1309 isEmptyElement = true;
1314 PushElementName (name);
1316 parserContext.PushScope ();
1321 XmlNodeType.Element, // nodeType
1325 isEmptyElement, // isEmptyElement
1327 false // clearAttributes
1329 if (prefix.Length > 0)
1330 currentToken.NamespaceURI = LookupNamespace (prefix, true);
1331 else if (namespaces)
1332 currentToken.NamespaceURI = parserContext.NamespaceManager.DefaultNamespace;
1335 if (NamespaceURI == null)
1336 throw NotWFError (String.Format ("'{0}' is undeclared namespace.", Prefix));
1338 for (int i = 0; i < attributeCount; i++) {
1339 MoveToAttribute (i);
1340 if (NamespaceURI == null)
1341 throw NotWFError (String.Format ("'{0}' is undeclared namespace.", Prefix));
1348 for (int i = 0; i < attributeCount; i++) {
1349 if (!Object.ReferenceEquals (attributeTokens [i].Prefix, XmlNamespaceManager.PrefixXml))
1351 string aname = attributeTokens [i].LocalName;
1352 string value = attributeTokens [i].Value;
1355 if (this.resolver != null) {
1357 BaseURI != String.Empty ?
1358 new Uri (BaseURI) : null;
1359 Uri uri = resolver.ResolveUri (
1361 parserContext.BaseURI =
1367 parserContext.BaseURI = value;
1370 parserContext.XmlLang = value;
1375 parserContext.XmlSpace = XmlSpace.Preserve;
1378 parserContext.XmlSpace = XmlSpace.Default;
1381 throw NotWFError (String.Format ("Invalid xml:space value: {0}", value));
1388 CheckCurrentStateUpdate ();
1391 private void PushElementName (string name)
1393 if (elementNames.Length == elementNameStackPos) {
1394 string [] newArray = new string [elementNames.Length * 2];
1395 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1396 elementNames = newArray;
1398 elementNames [elementNameStackPos++] = name;
1401 // The reader is positioned on the first character
1402 // of the element's name.
1403 private void ReadEndTag ()
1405 if (currentState != XmlNodeType.Element)
1406 throw NotWFError ("End tag cannot appear in this state.");
1408 currentLinkedNodeLineNumber = line;
1409 currentLinkedNodeLinePosition = column;
1411 string prefix, localName;
1412 string name = ReadName (out prefix, out localName);
1413 if (elementNameStackPos == 0)
1414 throw NotWFError ("closing element without matching opening element");
1415 string expected = elementNames [--elementNameStackPos];
1416 if (expected != name)
1417 throw NotWFError (String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1419 ExpectAfterWhitespace ('>');
1424 XmlNodeType.EndElement, // nodeType
1427 localName, // localName
1428 false, // isEmptyElement
1430 true // clearAttributes
1432 if (prefix.Length > 0)
1433 currentToken.NamespaceURI = LookupNamespace (prefix, true);
1434 else if (namespaces)
1435 currentToken.NamespaceURI = parserContext.NamespaceManager.DefaultNamespace;
1439 CheckCurrentStateUpdate ();
1442 private void CheckCurrentStateUpdate ()
1444 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1445 currentState = XmlNodeType.EndElement;
1448 private void AppendSurrogatePairNameChar (int ch)
1450 nameBuffer [nameLength++] = (char) ((ch - 0x10000) / 0x400 + 0xD800);
1451 if (nameLength == nameCapacity)
1452 ExpandNameCapacity ();
1453 nameBuffer [nameLength++] = (char) ((ch - 0x10000) % 0x400 + 0xDC00);
1456 private void ExpandNameCapacity ()
1458 nameCapacity = nameCapacity * 2;
1459 char [] oldNameBuffer = nameBuffer;
1460 nameBuffer = new char [nameCapacity];
1461 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1464 private void AppendValueChar (int ch)
1466 if (ch < Char.MaxValue)
1467 valueBuffer.Append ((char) ch);
1469 AppendSurrogatePairValueChar (ch);
1472 private void AppendSurrogatePairValueChar (int ch)
1474 valueBuffer.Append ((char) ((ch - 0x10000) / 0x400 + 0xD800));
1475 valueBuffer.Append ((char) ((ch - 0x10000) % 0x400 + 0xDC00));
1478 private string CreateValueString ()
1480 return (valueBuffer.Capacity < 100) ?
1481 valueBuffer.ToString (0, valueBuffer.Length) :
1482 valueBuffer.ToString ();
1485 private void ClearValueBuffer ()
1487 valueBuffer.Length = 0;
1490 private void AppendCurrentTagChar (int ch)
1492 if (currentTagLength == currentTagCapacity)
1493 ExpandCurrentTagCapacity ();
1494 if (ch < Char.MaxValue)
1495 currentTagBuffer [currentTagLength++] = (char) ch;
1497 currentTagBuffer [currentTagLength++] = (char) ((ch - 0x10000) / 0x400 + 0xD800);
1498 if (currentTagLength == currentTagCapacity)
1499 ExpandCurrentTagCapacity ();
1500 currentTagBuffer [currentTagLength++] = (char) ((ch - 0x10000) % 0x400 + 0xDC00);
1504 private void ExpandCurrentTagCapacity ()
1506 currentTagCapacity = currentTagCapacity * 2;
1507 char [] oldCurrentTagBuffer = currentTagBuffer;
1508 currentTagBuffer = new char [currentTagCapacity];
1509 Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1512 // The reader is positioned on the first character
1514 private void ReadText (bool notWhitespace)
1516 if (currentState != XmlNodeType.Element)
1517 throw NotWFError ("Text node cannot appear in this state.");
1520 ClearValueBuffer ();
1522 int ch = PeekChar ();
1523 bool previousWasCloseBracket = false;
1525 while (ch != '<' && ch != -1) {
1528 ch = ReadReference (false);
1529 if (returnEntityReference) // Returns -1 if char validation should not be done
1531 } else if (normalization && ch == '\r') {
1535 // append '\n' instead of '\r'.
1536 AppendValueChar ('\n');
1537 // and in case of "\r\n", discard '\r'.
1539 if (CharacterChecking && XmlChar.IsInvalid (ch))
1540 throw NotWFError ("Not allowed character was found.");
1544 // FIXME: it might be optimized by the JIT later,
1545 // AppendValueChar (ch);
1547 if (ch < Char.MaxValue)
1548 valueBuffer.Append ((char) ch);
1550 AppendSurrogatePairValueChar (ch);
1555 if (previousWasCloseBracket)
1556 if (PeekChar () == '>')
1557 throw NotWFError ("Inside text content, character sequence ']]>' is not allowed.");
1558 previousWasCloseBracket = true;
1560 else if (previousWasCloseBracket)
1561 previousWasCloseBracket = false;
1563 notWhitespace = true;
1566 if (returnEntityReference && valueBuffer.Length == 0) {
1567 SetEntityReferenceProperties ();
1569 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1570 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1572 nodeType, // nodeType
1573 String.Empty, // name
1574 String.Empty, // prefix
1575 String.Empty, // localName
1576 false, // isEmptyElement
1577 null, // value: create only when required
1578 true // clearAttributes
1583 // The leading '&' has already been consumed.
1584 // Returns true if the entity reference isn't a simple
1585 // character reference or one of the predefined entities.
1586 // This allows the ReadText method to break so that the
1587 // next call to Read will return the EntityReference node.
1588 private int ReadReference (bool ignoreEntityReferences)
1590 if (PeekChar () == '#') {
1592 return ReadCharacterReference ();
1594 return ReadEntityReference (ignoreEntityReferences);
1597 private int ReadCharacterReference ()
1601 if (PeekChar () == 'x') {
1604 while (PeekChar () != ';' && PeekChar () != -1) {
1605 int ch = ReadChar ();
1607 if (ch >= '0' && ch <= '9')
1608 value = (value << 4) + ch - '0';
1609 else if (ch >= 'A' && ch <= 'F')
1610 value = (value << 4) + ch - 'A' + 10;
1611 else if (ch >= 'a' && ch <= 'f')
1612 value = (value << 4) + ch - 'a' + 10;
1614 throw NotWFError (String.Format (CultureInfo.InvariantCulture,
1615 "invalid hexadecimal digit: {0} (#x{1:X})",
1620 while (PeekChar () != ';' && PeekChar () != -1) {
1621 int ch = ReadChar ();
1623 if (ch >= '0' && ch <= '9')
1624 value = value * 10 + ch - '0';
1626 throw NotWFError (String.Format (CultureInfo.InvariantCulture,
1627 "invalid decimal digit: {0} (#x{1:X})",
1635 // There is no way to save surrogate pairs...
1636 if (CharacterChecking && Normalization &&
1637 XmlChar.IsInvalid (value))
1638 throw NotWFError ("Referenced character was not allowed in XML. Normalization is " + normalization + ", checkCharacters = " + checkCharacters);
1642 // Returns -1 if it should not be validated.
1643 // Real EOF must not be detected here.
1644 private int ReadEntityReference (bool ignoreEntityReferences)
1646 string name = ReadName ();
1649 int predefined = XmlChar.GetPredefinedEntity (name);
1650 if (predefined >= 0)
1653 if (ignoreEntityReferences) {
1654 AppendValueChar ('&');
1655 for (int i = 0; i < name.Length; i++)
1656 AppendValueChar (name [i]);
1657 AppendValueChar (';');
1659 returnEntityReference = true;
1660 entityReferenceName = name;
1666 // The reader is positioned on the first character of
1667 // the attribute name.
1668 private void ReadAttributes (bool isXmlDecl)
1671 bool requireWhitespace = false;
1672 currentAttribute = -1;
1673 currentAttributeValue = -1;
1676 if (!SkipWhitespace () && requireWhitespace)
1677 throw NotWFError ("Unexpected token. Name is required here.");
1679 IncrementAttributeToken ();
1680 currentAttributeToken.LineNumber = line;
1681 currentAttributeToken.LinePosition = column;
1683 string prefix, localName;
1684 currentAttributeToken.Name = ReadName (out prefix, out localName);
1685 currentAttributeToken.Prefix = prefix;
1686 currentAttributeToken.LocalName = localName;
1687 ExpectAfterWhitespace ('=');
1689 ReadAttributeValueTokens (-1);
1690 // This hack is required for xmldecl which has
1691 // both effective attributes and Value.
1694 dummyValue = currentAttributeToken.Value;
1698 if (!SkipWhitespace ())
1699 requireWhitespace = true;
1700 peekChar = PeekChar ();
1702 if (peekChar == '?')
1705 else if (peekChar == '/' || peekChar == '>')
1707 } while (peekChar != -1);
1709 currentAttribute = -1;
1710 currentAttributeValue = -1;
1713 private void AddDtdAttribute (string name, string value)
1715 IncrementAttributeToken ();
1716 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1717 ati.Name = parserContext.NameTable.Add (name);
1718 ati.Prefix = String.Empty;
1719 ati.NamespaceURI = String.Empty;
1720 IncrementAttributeValueToken ();
1721 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1723 SetTokenProperties (vti,
1734 private void IncrementAttributeToken ()
1737 if (attributeTokens.Length == currentAttribute) {
1738 XmlAttributeTokenInfo [] newArray =
1739 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1740 attributeTokens.CopyTo (newArray, 0);
1741 attributeTokens = newArray;
1743 if (attributeTokens [currentAttribute] == null)
1744 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1745 currentAttributeToken = attributeTokens [currentAttribute];
1746 currentAttributeToken.Clear ();
1749 private void IncrementAttributeValueToken ()
1751 currentAttributeValue++;
1752 if (attributeValueTokens.Length == currentAttributeValue) {
1753 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1754 attributeValueTokens.CopyTo (newArray, 0);
1755 attributeValueTokens = newArray;
1757 if (attributeValueTokens [currentAttributeValue] == null)
1758 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this);
1759 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1760 currentAttributeValueToken.Clear ();
1763 // LAMESPEC: Orthodox XML reader should normalize attribute values
1764 private void ReadAttributeValueTokens (int dummyQuoteChar)
1766 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1768 if (quoteChar != '\'' && quoteChar != '\"')
1769 throw NotWFError ("an attribute value was not quoted");
1770 currentAttributeToken.QuoteChar = (char) quoteChar;
1772 IncrementAttributeValueToken ();
1773 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1774 currentAttributeValueToken.LineNumber = line;
1775 currentAttributeValueToken.LinePosition = column;
1777 bool incrementToken = false;
1778 bool isNewToken = true;
1781 currentAttributeValueToken.ValueBufferStart = valueBuffer.Length;
1784 if (ch == quoteChar)
1787 if (incrementToken) {
1788 IncrementAttributeValueToken ();
1789 currentAttributeValueToken.ValueBufferStart = valueBuffer.Length;
1790 currentAttributeValueToken.LineNumber = line;
1791 currentAttributeValueToken.LinePosition = column;
1792 incrementToken = false;
1799 throw NotWFError ("attribute values cannot contain '<'");
1801 if (dummyQuoteChar < 0)
1802 throw NotWFError ("unexpected end of file in an attribute value");
1803 else // Attribute value constructor.
1809 if (PeekChar () == '\n')
1810 continue; // skip '\r'.
1812 // The csc in MS.NET 2.0 beta 1 barfs on this goto, so work around that
1821 // When Normalize = true, then replace
1822 // all spaces to ' '
1828 if (PeekChar () == '#') {
1830 ch = ReadCharacterReference ();
1831 AppendValueChar (ch);
1834 // Check XML 1.0 section 3.1 WFC.
1835 string entName = ReadName ();
1837 int predefined = XmlChar.GetPredefinedEntity (entName);
1838 if (predefined < 0) {
1839 CheckAttributeEntityReferenceWFC (entName);
1841 if (entityHandling == EntityHandling.ExpandEntities) {
1842 string value = DTD.GenerateEntityAttributeText (entName);
1843 foreach (char c in value)
1844 AppendValueChar (c);
1848 currentAttributeValueToken.ValueBufferEnd = valueBuffer.Length;
1849 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1851 IncrementAttributeValueToken ();
1852 currentAttributeValueToken.Name = entName;
1853 currentAttributeValueToken.Value = String.Empty;
1854 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1855 incrementToken = true;
1859 AppendValueChar (predefined);
1862 if (CharacterChecking && XmlChar.IsInvalid (ch))
1863 throw NotWFError ("Invalid character was found.");
1864 // FIXME: it might be optimized by the JIT later,
1865 // AppendValueChar (ch);
1867 if (ch < Char.MaxValue)
1868 valueBuffer.Append ((char) ch);
1870 AppendSurrogatePairValueChar (ch);
1877 if (!incrementToken) {
1878 currentAttributeValueToken.ValueBufferEnd = valueBuffer.Length;
1879 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1881 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1885 private void CheckAttributeEntityReferenceWFC (string entName)
1887 DTDEntityDeclaration entDecl =
1888 DTD == null ? null : DTD.EntityDecls [entName];
1889 if (entDecl == null) {
1890 if (entityHandling == EntityHandling.ExpandEntities
1891 || (DTD != null && resolver != null && entDecl == null))
1892 throw NotWFError (String.Format ("Referenced entity '{0}' does not exist.", entName));
1897 if (entDecl.HasExternalReference)
1898 throw NotWFError ("Reference to external entities is not allowed in the value of an attribute.");
1899 if (isStandalone && !entDecl.IsInternalSubset)
1900 throw NotWFError ("Reference to external entities is not allowed in the internal subset.");
1901 if (entDecl.EntityValue.IndexOf ('<') >= 0)
1902 throw NotWFError ("Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1905 // The reader is positioned on the first character
1908 // It may be xml declaration or processing instruction.
1909 private void ReadProcessingInstruction ()
1911 string target = ReadName ();
1912 if (target == "xml") {
1913 ReadXmlDeclaration ();
1915 } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml")
1916 throw NotWFError ("Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1918 if (currentState == XmlNodeType.None)
1919 currentState = XmlNodeType.XmlDeclaration;
1921 if (!SkipWhitespace ())
1922 if (PeekChar () != '?')
1923 throw NotWFError ("Invalid processing instruction name was found.");
1925 ClearValueBuffer ();
1927 while (PeekChar () != -1) {
1928 int ch = ReadChar ();
1930 if (ch == '?' && PeekChar () == '>') {
1935 if (CharacterChecking && XmlChar.IsInvalid (ch))
1936 throw NotWFError ("Invalid character was found.");
1937 AppendValueChar (ch);
1941 XmlNodeType.ProcessingInstruction, // nodeType
1943 String.Empty, // prefix
1944 target, // localName
1945 false, // isEmptyElement
1946 null, // value: create only when required
1947 true // clearAttributes
1951 // The reader is positioned after "<?xml "
1952 private void ReadXmlDeclaration ()
1954 if (currentState != XmlNodeType.None) {
1955 throw NotWFError ("XML declaration cannot appear in this state.");
1957 currentState = XmlNodeType.XmlDeclaration;
1961 ReadAttributes (true); // They must have "version."
1962 string version = GetAttribute ("version");
1964 string message = null;
1966 if (attributeTokens [0].Name != "version" || version != "1.0")
1967 message = "Version 1.0 declaration is required in XML Declaration.";
1968 else if (attributeCount > 1 &&
1969 (attributeTokens [1].Name != "encoding" &&
1970 attributeTokens [1].Name != "standalone"))
1971 message = "Invalid Xml Declaration markup was found.";
1972 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1973 message = "Invalid Xml Declaration markup was found.";
1974 string sa = GetAttribute ("standalone");
1975 if (sa != null && sa != "yes" && sa != "no")
1976 message = String.Format ("Only 'yes' or 'no' is allowed for standalone. Value was '{0}'", sa);
1978 this.isStandalone = (sa == "yes");
1980 if (message != null)
1981 throw NotWFError (message);
1984 XmlNodeType.XmlDeclaration, // nodeType
1986 String.Empty, // prefix
1988 false, // isEmptyElement
1989 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1990 false // clearAttributes
1996 private void SkipTextDeclaration ()
1998 this.currentState = XmlNodeType.Element;
2000 if (PeekChar () != '<')
2005 if (PeekChar () != '?') {
2011 while (peekCharsIndex < 6) {
2012 if (PeekChar () < 0)
2017 if (new string (peekChars, 2, 4) != "xml ") {
2018 if (new string (peekChars, 2, 4).ToLower (CultureInfo.InvariantCulture) == "xml ") {
2019 throw NotWFError ("Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
2028 if (PeekChar () == 'v') {
2030 ExpectAfterWhitespace ('=');
2032 int quoteChar = ReadChar ();
2033 char [] expect1_0 = new char [3];
2034 int versionLength = 0;
2035 switch (quoteChar) {
2038 while (PeekChar () != quoteChar) {
2039 if (PeekChar () == -1)
2040 throw NotWFError ("Invalid version declaration inside text declaration.");
2041 else if (versionLength == 3)
2042 throw NotWFError ("Invalid version number inside text declaration.");
2044 expect1_0 [versionLength] = (char) ReadChar ();
2046 if (versionLength == 3 && new String (expect1_0) != "1.0")
2047 throw NotWFError ("Invalid version number inside text declaration.");
2054 throw NotWFError ("Invalid version declaration inside text declaration.");
2058 if (PeekChar () == 'e') {
2059 Expect ("encoding");
2060 ExpectAfterWhitespace ('=');
2062 int quoteChar = ReadChar ();
2063 switch (quoteChar) {
2066 while (PeekChar () != quoteChar)
2067 if (ReadChar () == -1)
2068 throw NotWFError ("Invalid encoding declaration inside text declaration.");
2073 throw NotWFError ("Invalid encoding declaration inside text declaration.");
2075 // Encoding value should be checked inside XmlInputStream.
2078 throw NotWFError ("Encoding declaration is mandatory in text declaration.");
2083 // The reader is positioned on the first character after
2084 // the leading '<!'.
2085 private void ReadDeclaration ()
2087 int ch = PeekChar ();
2105 throw NotWFError ("Unexpected declaration markup was found.");
2109 // The reader is positioned on the first character after
2110 // the leading '<!--'.
2111 private void ReadComment ()
2113 if (currentState == XmlNodeType.None)
2114 currentState = XmlNodeType.XmlDeclaration;
2116 ClearValueBuffer ();
2118 while (PeekChar () != -1) {
2119 int ch = ReadChar ();
2121 if (ch == '-' && PeekChar () == '-') {
2124 if (PeekChar () != '>')
2125 throw NotWFError ("comments cannot contain '--'");
2131 if (XmlChar.IsInvalid (ch))
2132 throw NotWFError ("Not allowed character was found.");
2134 AppendValueChar (ch);
2138 XmlNodeType.Comment, // nodeType
2139 String.Empty, // name
2140 String.Empty, // prefix
2141 String.Empty, // localName
2142 false, // isEmptyElement
2143 null, // value: create only when required
2144 true // clearAttributes
2148 // The reader is positioned on the first character after
2149 // the leading '<![CDATA['.
2150 private void ReadCDATA ()
2152 if (currentState != XmlNodeType.Element)
2153 throw NotWFError ("CDATA section cannot appear in this state.");
2155 ClearValueBuffer ();
2159 while (PeekChar () != -1) {
2164 if (ch == ']' && PeekChar () == ']') {
2165 ch = ReadChar (); // ']'
2167 if (PeekChar () == '>') {
2174 if (normalization && ch == '\r') {
2177 // append '\n' instead of '\r'.
2178 AppendValueChar ('\n');
2179 // otherwise, discard '\r'.
2182 if (CharacterChecking && XmlChar.IsInvalid (ch))
2183 throw NotWFError ("Invalid character was found.");
2185 // FIXME: it might be optimized by the JIT later,
2186 // AppendValueChar (ch);
2188 if (ch < Char.MaxValue)
2189 valueBuffer.Append ((char) ch);
2191 AppendSurrogatePairValueChar (ch);
2196 XmlNodeType.CDATA, // nodeType
2197 String.Empty, // name
2198 String.Empty, // prefix
2199 String.Empty, // localName
2200 false, // isEmptyElement
2201 null, // value: create only when required
2202 true // clearAttributes
2206 // The reader is positioned on the first character after
2207 // the leading '<!DOCTYPE'.
2208 private void ReadDoctypeDecl ()
2211 throw NotWFError ("Document Type Declaration (DTD) is prohibited in this XML.");
2212 switch (currentState) {
2213 case XmlNodeType.DocumentType:
2214 case XmlNodeType.Element:
2215 case XmlNodeType.EndElement:
2216 throw NotWFError ("Document type cannot appear in this state.");
2218 currentState = XmlNodeType.DocumentType;
2220 string doctypeName = null;
2221 string publicId = null;
2222 string systemId = null;
2223 int intSubsetStartLine = 0;
2224 int intSubsetStartColumn = 0;
2227 doctypeName = ReadName ();
2232 systemId = ReadSystemLiteral (true);
2235 publicId = ReadPubidLiteral ();
2236 if (!SkipWhitespace ())
2237 throw NotWFError ("Whitespace is required between PUBLIC id and SYSTEM id.");
2238 systemId = ReadSystemLiteral (false);
2244 if(PeekChar () == '[')
2246 // read markupdecl etc. or end of decl
2248 intSubsetStartLine = this.LineNumber;
2249 intSubsetStartColumn = this.LinePosition;
2250 int startPos = currentTagLength;
2251 ReadInternalSubset ();
2252 int endPos = currentTagLength - 1;
2253 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2255 // end of DOCTYPE decl.
2256 ExpectAfterWhitespace ('>');
2258 GenerateDTDObjectModel (doctypeName, publicId,
2259 systemId, parserContext.InternalSubset,
2260 intSubsetStartLine, intSubsetStartColumn);
2262 // set properties for <!DOCTYPE> node
2264 XmlNodeType.DocumentType, // nodeType
2265 doctypeName, // name
2266 String.Empty, // prefix
2267 doctypeName, // localName
2268 false, // isEmptyElement
2269 parserContext.InternalSubset, // value
2270 true // clearAttributes
2273 if (publicId != null)
2274 AddDtdAttribute ("PUBLIC", publicId);
2275 if (systemId != null)
2276 AddDtdAttribute ("SYSTEM", systemId);
2277 currentAttribute = currentAttributeValue = -1;
2280 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2281 string systemId, string internalSubset)
2283 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2286 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2287 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2290 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2291 DTD.BaseURI = BaseURI;
2293 DTD.PublicId = publicId;
2294 DTD.SystemId = systemId;
2295 DTD.InternalSubset = internalSubset;
2296 DTD.XmlResolver = resolver;
2297 DTD.IsStandalone = isStandalone;
2298 DTD.LineNumber = line;
2299 DTD.LinePosition = column;
2301 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2302 dr.Normalization = this.normalization;
2303 return dr.GenerateDTDObjectModel ();
2306 private enum DtdInputState
2319 private class DtdInputStateStack
2321 Stack intern = new Stack ();
2322 public DtdInputStateStack ()
2324 Push (DtdInputState.Free);
2327 public DtdInputState Peek ()
2329 return (DtdInputState) intern.Peek ();
2332 public DtdInputState Pop ()
2334 return (DtdInputState) intern.Pop ();
2337 public void Push (DtdInputState val)
2344 DtdInputStateStack stateStack = new DtdInputStateStack ();
2345 DtdInputState State {
2346 get { return stateStack.Peek (); }
2349 // Simply read but not generate any result.
2350 private void ReadInternalSubset ()
2352 bool continueParse = true;
2354 while (continueParse) {
2355 switch (ReadChar ()) {
2358 case DtdInputState.Free:
2359 continueParse = false;
2361 case DtdInputState.InsideDoubleQuoted:
2363 case DtdInputState.InsideSingleQuoted:
2366 throw NotWFError ("unexpected end of file at DTD.");
2370 throw NotWFError ("unexpected end of file at DTD.");
2373 case DtdInputState.InsideDoubleQuoted:
2374 case DtdInputState.InsideSingleQuoted:
2375 case DtdInputState.Comment:
2376 continue; // well-formed
2378 int c = ReadChar ();
2381 stateStack.Push (DtdInputState.PI);
2384 switch (ReadChar ()) {
2386 switch (ReadChar ()) {
2389 stateStack.Push (DtdInputState.ElementDecl);
2393 stateStack.Push (DtdInputState.EntityDecl);
2396 throw NotWFError ("unexpected token '<!E'.");
2401 stateStack.Push (DtdInputState.AttlistDecl);
2405 stateStack.Push (DtdInputState.NotationDecl);
2409 stateStack.Push (DtdInputState.Comment);
2414 throw NotWFError (String.Format ("unexpected '<{0}'.", (char) c));
2418 if (State == DtdInputState.InsideSingleQuoted)
2420 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2421 stateStack.Push (DtdInputState.InsideSingleQuoted);
2424 if (State == DtdInputState.InsideDoubleQuoted)
2426 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2427 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2431 case DtdInputState.ElementDecl:
2432 goto case DtdInputState.NotationDecl;
2433 case DtdInputState.AttlistDecl:
2434 goto case DtdInputState.NotationDecl;
2435 case DtdInputState.EntityDecl:
2436 goto case DtdInputState.NotationDecl;
2437 case DtdInputState.NotationDecl:
2440 case DtdInputState.InsideDoubleQuoted:
2441 case DtdInputState.InsideSingleQuoted:
2442 case DtdInputState.Comment:
2445 throw NotWFError ("unexpected token '>'");
2449 if (State == DtdInputState.PI) {
2450 if (ReadChar () == '>')
2455 if (State == DtdInputState.Comment) {
2456 if (PeekChar () == '-') {
2464 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2465 throw NotWFError ("Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2471 // The reader is positioned on the first 'S' of "SYSTEM".
2472 private string ReadSystemLiteral (bool expectSYSTEM)
2476 if (!SkipWhitespace ())
2477 throw NotWFError ("Whitespace is required after 'SYSTEM'.");
2481 int quoteChar = ReadChar (); // apos or quot
2483 ClearValueBuffer ();
2484 while (c != quoteChar) {
2487 throw NotWFError ("Unexpected end of stream in ExternalID.");
2489 AppendValueChar (c);
2491 return CreateValueString ();
2494 private string ReadPubidLiteral()
2497 if (!SkipWhitespace ())
2498 throw NotWFError ("Whitespace is required after 'PUBLIC'.");
2499 int quoteChar = ReadChar ();
2501 ClearValueBuffer ();
2502 while(c != quoteChar)
2505 if(c < 0) throw NotWFError ("Unexpected end of stream in ExternalID.");
2506 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2507 throw NotWFError (String.Format ("character '{0}' not allowed for PUBLIC ID", (char)c ));
2509 AppendValueChar (c);
2511 return CreateValueString ();
2514 // The reader is positioned on the first character
2516 private string ReadName ()
2518 string prefix, local;
2519 return ReadName (out prefix, out local);
2522 private string ReadName (out string prefix, out string localName)
2524 // FIXME: need to reject non-QName names?
2526 int ch = PeekChar ();
2527 if (!XmlChar.IsFirstNameChar (ch))
2528 throw NotWFError (String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
2533 // AppendNameChar (ch);
2535 if (nameLength == nameCapacity)
2536 ExpandNameCapacity ();
2537 if (ch < Char.MaxValue)
2538 nameBuffer [nameLength++] = (char) ch;
2540 AppendSurrogatePairNameChar (ch);
2545 while (XmlChar.IsNameChar (PeekChar ())) {
2548 if (namespaces && colonAt < 0 && ch == ':')
2549 colonAt = nameLength;
2550 // AppendNameChar (ch);
2552 if (nameLength == nameCapacity)
2553 ExpandNameCapacity ();
2554 if (ch < Char.MaxValue)
2555 nameBuffer [nameLength++] = (char) ch;
2557 AppendSurrogatePairNameChar (ch);
2561 string name = parserContext.NameTable.Add (nameBuffer, 0, nameLength);
2563 if (namespaces && colonAt > 0) {
2564 prefix = parserContext.NameTable.Add (nameBuffer, 0, colonAt);
2565 localName = parserContext.NameTable.Add (nameBuffer, colonAt + 1, nameLength - colonAt - 1);
2568 prefix = String.Empty;
2575 // Read the next character and compare it against the
2576 // specified character.
2577 private void Expect (int expected)
2579 int ch = ReadChar ();
2581 if (ch != expected) {
2582 throw NotWFError (String.Format (CultureInfo.InvariantCulture,
2583 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2586 ch < 0 ? (object) "EOF" : (char) ch,
2591 private void Expect (string expected)
2593 int len = expected.Length;
2594 for(int i=0; i< len; i++)
2595 Expect (expected[i]);
2598 private void ExpectAfterWhitespace (char c)
2601 int i = ReadChar ();
2602 if (i < 0x21 && XmlChar.IsWhitespace (i))
2605 throw NotWFError (String.Format (CultureInfo.InvariantCulture, "Expected {0}, but found {1} [{2}]", c, i < 0 ? (object) "EOF" : (char) i, i));
2610 // Does not consume the first non-whitespace character.
2611 private bool SkipWhitespace ()
2613 // FIXME: It should be inlined by the JIT.
2614 // bool skipped = XmlChar.IsWhitespace (PeekChar ());
2615 int ch = PeekChar ();
2616 bool skipped = (ch == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD);
2620 // FIXME: It should be inlined by the JIT.
2621 // while (XmlChar.IsWhitespace (PeekChar ()))
2623 while ((ch = PeekChar ()) == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD)
2628 private void ReadWhitespace ()
2630 if (currentState == XmlNodeType.None)
2631 currentState = XmlNodeType.XmlDeclaration;
2633 ClearValueBuffer ();
2634 int ch = PeekChar ();
2636 // FIXME: it might be optimized by the JIT later,
2637 // AppendValueChar (ReadChar ());
2640 if (ch < Char.MaxValue)
2641 valueBuffer.Append ((char) ch);
2643 AppendSurrogatePairValueChar (ch);
2645 // FIXME: It should be inlined by the JIT.
2646 // } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2648 } while (ch == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD);
2650 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2653 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2654 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2655 SetProperties (nodeType,
2660 null, // value: create only when required
2667 // Returns -1 if it should throw an error.
2668 private int ReadCharsInternal (char [] buffer, int offset, int length)
2670 shouldSkipUntilEndTag = true;
2672 int bufIndex = offset;
2673 for (int i = 0; i < length; i++) {
2674 int c = PeekChar ();
2677 throw NotWFError ("Unexpected end of xml.");
2680 if (PeekChar () != '/') {
2681 buffer [bufIndex++] = '<';
2684 // Seems to skip immediate EndElement
2691 shouldSkipUntilEndTag = false;
2692 Read (); // move to the next node
2696 if (c < Char.MaxValue)
2697 buffer [bufIndex++] = (char) c;
2699 buffer [bufIndex++] = (char) ((c - 0x10000) / 0x400 + 0xD800);
2700 buffer [bufIndex++] = (char) ((c - 0x10000) % 0x400 + 0xDC00);
2708 private bool ReadUntilEndTag ()
2711 currentState = XmlNodeType.EndElement;
2717 throw NotWFError ("Unexpected end of xml.");
2719 if (PeekChar () != '/')
2722 string name = ReadName ();
2723 if (name != elementNames [elementNameStackPos - 1])
2727 elementNames [--elementNameStackPos] = null;