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/
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 // Some thought needs to be given to performance.
37 // If current node is on an Attribute, Prefix might be null, and
38 // in several fields which uses XmlReader, it should be considered.
42 using System.Collections;
43 using System.Globalization;
45 using System.Security.Policy;
47 using System.Xml.Schema;
53 public class XmlTextReader : XmlReader,
54 IXmlLineInfo, IXmlNamespaceResolver
56 public class XmlTextReader : XmlReader, IXmlLineInfo
61 protected XmlTextReader ()
65 public XmlTextReader (Stream input)
66 : this (new XmlStreamReader (input))
70 public XmlTextReader (string url)
71 : this(url, new NameTable ())
75 public XmlTextReader (TextReader input)
76 : this (input, new NameTable ())
80 protected XmlTextReader (XmlNameTable nt)
81 : this (String.Empty, null, XmlNodeType.None, null)
85 public XmlTextReader (Stream input, XmlNameTable nt)
86 : this(new XmlStreamReader (input), nt)
90 public XmlTextReader (string url, Stream input)
91 : this (url, new XmlStreamReader (input))
95 public XmlTextReader (string url, TextReader input)
96 : this (url, input, new NameTable ())
100 public XmlTextReader (string url, XmlNameTable nt)
102 Uri uri = resolver.ResolveUri (null, url);
103 Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
104 XmlParserContext ctx = new XmlParserContext (nt,
105 new XmlNamespaceManager (nt),
108 this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document);
111 public XmlTextReader (TextReader input, XmlNameTable nt)
112 : this (String.Empty, input, nt)
116 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
117 : this (context != null ? context.BaseURI : String.Empty,
118 new XmlStreamReader (xmlFragment),
124 public XmlTextReader (string url, Stream input, XmlNameTable nt)
125 : this (url, new XmlStreamReader (input), nt)
129 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
130 : this (url, input, XmlNodeType.Document, null)
134 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
135 : this (context != null ? context.BaseURI : String.Empty,
136 new StringReader (xmlFragment),
142 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
144 InitializeContext (url, context, fragment, fragType);
151 public override int AttributeCount
153 get { return attributeCount; }
156 public override string BaseURI
158 get { return parserContext.BaseURI; }
162 public override bool CanResolveEntity {
168 internal bool CharacterChecking {
169 get { return checkCharacters && normalization; }
170 set { checkCharacters = false; }
173 // for XmlReaderSettings.CloseInput support
174 internal bool CloseInput {
175 get { return closeInput; }
176 set { closeInput = value; }
179 public override int Depth
182 int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element ? 0 : -1;
183 if (currentAttributeValue >= 0)
184 return nodeTypeMod + elementDepth + 2; // inside attribute value.
185 else if (currentAttribute >= 0)
186 return nodeTypeMod + elementDepth + 1;
191 public Encoding Encoding
193 get { return parserContext.Encoding; }
197 public EntityHandling EntityHandling {
198 get { return entityHandling; }
199 set { entityHandling = value; }
203 public override bool EOF {
204 get { return readState == ReadState.EndOfFile; }
209 public override Evidence Evidence {
210 get { return base.Evidence; }
214 public override bool HasValue {
215 get { return cursorToken.Value != null; }
218 public override bool IsDefault {
219 // XmlTextReader does not expand default attributes.
220 get { return false; }
223 public override bool IsEmptyElement {
224 get { return cursorToken.IsEmptyElement; }
227 public override string this [int i] {
228 get { return GetAttribute (i); }
231 public override string this [string name] {
232 get { return GetAttribute (name); }
235 public override string this [string localName, string namespaceName] {
236 get { return GetAttribute (localName, namespaceName); }
239 public int LineNumber {
241 if (useProceedingLineInfo)
244 return cursorToken.LineNumber;
248 public int LinePosition {
250 if (useProceedingLineInfo)
253 return cursorToken.LinePosition;
257 public override string LocalName {
258 get { return cursorToken.LocalName; }
261 public override string Name {
262 get { return cursorToken.Name; }
265 public bool Namespaces {
266 get { return namespaces; }
268 if (readState != ReadState.Initial)
269 throw new InvalidOperationException ("Namespaces have to be set before reading.");
274 public override string NamespaceURI {
275 get { return cursorToken.NamespaceURI; }
278 public override XmlNameTable NameTable {
279 get { return parserContext.NameTable; }
282 public override XmlNodeType NodeType {
283 get { return cursorToken.NodeType; }
286 public bool Normalization {
287 get { return normalization; }
288 set { normalization = value; }
291 public override string Prefix {
292 get { return cursorToken.Prefix; }
296 public bool ProhibitDtd {
297 get { return prohibitDtd; }
298 set { prohibitDtd = value; }
302 public override char QuoteChar {
303 get { return cursorToken.QuoteChar; }
306 public override ReadState ReadState {
307 get { return readState; }
311 public override XmlReaderSettings Settings {
312 get { return base.Settings; }
316 public override string Value {
317 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
320 public WhitespaceHandling WhitespaceHandling {
321 get { return whitespaceHandling; }
322 set { whitespaceHandling = value; }
325 public override string XmlLang {
326 get { return parserContext.XmlLang; }
329 public XmlResolver XmlResolver {
330 set { resolver = value; }
333 public override XmlSpace XmlSpace {
334 get { return parserContext.XmlSpace; }
341 public override void Close ()
343 readState = ReadState.Closed;
345 cursorToken.Clear ();
346 currentToken.Clear ();
348 if (closeInput && reader != null)
352 public override string GetAttribute (int i)
354 if (i >= attributeCount)
355 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
357 return attributeTokens [i].Value;
361 // MS.NET 1.0 msdn says that this method returns String.Empty
362 // for absent attribute, but in fact it returns null.
363 // This description is corrected in MS.NET 1.1 msdn.
364 public override string GetAttribute (string name)
366 for (int i = 0; i < attributeCount; i++)
367 if (attributeTokens [i].Name == name)
368 return attributeTokens [i].Value;
372 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
374 for (int i = 0; i < attributeCount; i++) {
375 XmlAttributeTokenInfo ti = attributeTokens [i];
376 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
382 internal XmlParserContext GetInternalParserContext ()
384 return parserContext;
387 public override string GetAttribute (string localName, string namespaceURI)
389 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
392 return attributeTokens [idx].Value;
396 public IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
398 return parserContext.NamespaceManager.GetNamespacesInScope (scope);
402 public TextReader GetRemainder ()
404 if (peekCharsIndex == peekCharsLength)
406 return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
410 public bool HasLineInfo ()
412 bool IXmlLineInfo.HasLineInfo ()
418 public override string LookupNamespace (string prefix)
420 return LookupNamespace (prefix, false);
424 public override string LookupNamespace (string prefix, bool atomizedName)
426 internal override string LookupNamespace (string prefix, bool atomizedName)
429 return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
433 string IXmlNamespaceResolver.LookupPrefix (string ns)
435 return LookupPrefix (ns, false);
438 public string LookupPrefix (string ns, bool atomizedName)
440 return parserContext.NamespaceManager.LookupPrefix (ns, atomizedName);
444 public override void MoveToAttribute (int i)
446 if (i >= attributeCount)
447 throw new ArgumentOutOfRangeException ("attribute index out of range.");
449 currentAttribute = i;
450 currentAttributeValue = -1;
451 cursorToken = attributeTokens [i];
454 public override bool MoveToAttribute (string name)
456 for (int i = 0; i < attributeCount; i++) {
457 XmlAttributeTokenInfo ti = attributeTokens [i];
458 if (ti.Name == name) {
466 public override bool MoveToAttribute (string localName, string namespaceName)
468 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
471 MoveToAttribute (idx);
475 public override bool MoveToElement ()
477 if (currentToken == null) // for attribute .ctor()
480 if (cursorToken == currentToken)
483 if (currentAttribute >= 0) {
484 currentAttribute = -1;
485 currentAttributeValue = -1;
486 cursorToken = currentToken;
493 public override bool MoveToFirstAttribute ()
495 if (attributeCount == 0)
498 return MoveToNextAttribute ();
501 public override bool MoveToNextAttribute ()
503 if (currentAttribute == 0 && attributeCount == 0)
505 if (currentAttribute + 1 < attributeCount) {
507 currentAttributeValue = -1;
508 cursorToken = attributeTokens [currentAttribute];
515 public override bool Read ()
517 if (startNodeType == XmlNodeType.Attribute) {
518 if (currentAttribute == 0)
519 return false; // already read.
521 IncrementAttributeToken ();
522 ReadAttributeValueTokens ('"');
523 cursorToken = attributeTokens [0];
524 currentAttributeValue = -1;
525 readState = ReadState.Interactive;
530 readState = ReadState.Interactive;
531 currentLinkedNodeLineNumber = line;
532 currentLinkedNodeLinePosition = column;
533 useProceedingLineInfo = true;
535 cursorToken = currentToken;
537 currentAttribute = currentAttributeValue = -1;
538 currentToken.Clear ();
540 // It was moved from end of ReadStartTag ().
546 if (shouldSkipUntilEndTag) {
547 shouldSkipUntilEndTag = false;
548 return ReadUntilEndTag ();
551 base64CacheStartsAt = -1;
553 more = ReadContent ();
555 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
556 throw new XmlException ("Document element did not appear.");
558 useProceedingLineInfo = false;
562 public override bool ReadAttributeValue ()
564 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
568 if (currentAttribute < 0)
570 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
571 if (currentAttributeValue < 0)
572 currentAttributeValue = ti.ValueTokenStartIndex - 1;
574 if (currentAttributeValue < ti.ValueTokenEndIndex) {
575 currentAttributeValue++;
576 cursorToken = attributeValueTokens [currentAttributeValue];
583 private int SkipIgnorableBase64Chars (char [] chars, int charsLength, int i)
585 while (chars [i] == '=' || XmlChar.IsWhitespace (chars [i]))
586 if (charsLength == ++i)
591 public int ReadBase64 (byte [] buffer, int offset, int length)
594 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
596 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
597 else if (buffer.Length < offset + length)
598 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
600 if (length == 0) // It does not raise an error.
603 int bufIndex = offset;
604 int bufLast = offset + length;
606 if (base64CacheStartsAt >= 0) {
607 for (int i = base64CacheStartsAt; i < 3; i++) {
608 buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
609 if (bufIndex == bufLast)
610 return bufLast - offset;
614 for (int i = 0; i < 3; i++)
616 base64CacheStartsAt = -1;
618 int max = (int) System.Math.Ceiling (4.0 / 3 * length);
619 int additional = max % 4;
621 max += 4 - additional;
622 char [] chars = new char [max];
623 int charsLength = ReadChars (chars, 0, max);
628 for (int i = 0; i < charsLength - 3; i++) {
629 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
631 b = (byte) (GetBase64Byte (chars [i]) << 2);
632 if (bufIndex < bufLast)
633 buffer [bufIndex] = b;
635 if (base64CacheStartsAt < 0)
636 base64CacheStartsAt = 0;
639 // charsLength mod 4 might not equals to 0.
640 if (++i == charsLength)
642 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
644 b = GetBase64Byte (chars [i]);
645 work = (byte) (b >> 4);
646 if (bufIndex < bufLast) {
647 buffer [bufIndex] += work;
651 base64Cache [0] += work;
653 work = (byte) ((b & 0xf) << 4);
654 if (bufIndex < bufLast) {
655 buffer [bufIndex] = work;
658 if (base64CacheStartsAt < 0)
659 base64CacheStartsAt = 1;
660 base64Cache [1] = work;
663 if (++i == charsLength)
665 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
667 b = GetBase64Byte (chars [i]);
668 work = (byte) (b >> 2);
669 if (bufIndex < bufLast) {
670 buffer [bufIndex] += work;
674 base64Cache [1] += work;
676 work = (byte) ((b & 3) << 6);
677 if (bufIndex < bufLast)
678 buffer [bufIndex] = work;
680 if (base64CacheStartsAt < 0)
681 base64CacheStartsAt = 2;
682 base64Cache [2] = work;
684 if (++i == charsLength)
686 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
688 work = GetBase64Byte (chars [i]);
689 if (bufIndex < bufLast) {
690 buffer [bufIndex] += work;
694 base64Cache [2] += work;
696 return System.Math.Min (bufLast - offset, bufIndex - offset);
699 public int ReadBinHex (byte [] buffer, int offset, int length)
702 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
704 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
705 else if (buffer.Length < offset + length)
706 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
711 char [] chars = new char [length * 2];
712 int charsLength = ReadChars (chars, 0, length * 2);
713 return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
716 public int ReadChars (char [] buffer, int offset, int length)
718 return ReadCharsInternal (buffer, offset, length);
722 public override string ReadString ()
724 return ReadStringInternal ();
728 public override string ReadInnerXml ()
730 return ReadInnerXmlInternal ();
733 public override string ReadOuterXml ()
735 return ReadOuterXmlInternal ();
738 public override string ReadString ()
740 return ReadStringInternal ();
744 public void ResetState ()
746 throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment.");
752 public override bool ReadValueAsBoolean ()
754 return base.ReadValueAsBoolean ();
758 public override DateTime ReadValueAsDateTime ()
760 return base.ReadValueAsDateTime ();
764 public override decimal ReadValueAsDecimal ()
766 return base.ReadValueAsDecimal ();
770 public override double ReadValueAsDouble ()
772 return base.ReadValueAsDouble ();
776 public override int ReadValueAsInt32 ()
778 return base.ReadValueAsInt32 ();
782 public override long ReadValueAsInt64 ()
784 return base.ReadValueAsInt64 ();
788 public override ICollection ReadValueAsList ()
790 return base.ReadValueAsList ();
794 public override float ReadValueAsSingle ()
796 return base.ReadValueAsSingle ();
800 public override string ReadValueAsString ()
802 return ReadString ();
806 public override object ReadValueAs (Type type)
808 return base.ReadValueAs (type);
812 public override object ReadValueAs (Type type, IXmlNamespaceResolver resolver)
814 return base.ReadValueAs (type, resolver);
818 public override void ResolveEntity ()
820 // XmlTextReader does not resolve entities.
821 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
825 [MonoTODO ("Implement for performance reason")]
826 public override void Skip ()
834 // Parsed DTD Objects
835 #if DTD_HANDLE_EVENTS
836 internal event ValidationEventHandler ValidationEventHandler;
839 internal DTDObjectModel DTD {
840 get { return parserContext.Dtd; }
843 internal XmlResolver Resolver {
844 get { return resolver; }
849 internal class XmlTokenInfo
851 public XmlTokenInfo (XmlTextReader xtr, bool isPrimaryToken)
853 this.isPrimaryToken = isPrimaryToken;
861 protected XmlTextReader Reader;
864 public string LocalName;
865 public string Prefix;
866 public string NamespaceURI;
867 public bool IsEmptyElement;
868 public char QuoteChar;
869 public int LineNumber;
870 public int LinePosition;
872 public XmlNodeType NodeType;
874 public virtual string Value {
876 if (valueCache != null)
879 case XmlNodeType.Text:
880 case XmlNodeType.SignificantWhitespace:
881 case XmlNodeType.Whitespace:
882 case XmlNodeType.Comment:
883 case XmlNodeType.CDATA:
884 case XmlNodeType.ProcessingInstruction:
885 valueCache = Reader.CreateValueString ();
890 set { valueCache = value; }
893 public virtual void Clear ()
896 NodeType = XmlNodeType.None;
897 Name = LocalName = Prefix = NamespaceURI = String.Empty;
898 IsEmptyElement = false;
900 LineNumber = LinePosition = 0;
903 internal virtual void FillNames ()
905 if (Reader.Namespaces) {
906 int indexOfColon = -1;
908 case XmlNodeType.Attribute:
909 case XmlNodeType.Element:
910 case XmlNodeType.EndElement:
911 indexOfColon = Name.IndexOf (':');
915 if (indexOfColon == -1) {
916 Prefix = String.Empty;
919 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
920 // However, this might be reverted if NameTable is got improved.
921 char [] nameArr = Name.ToCharArray ();
922 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
923 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
924 // Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
925 // LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
930 case XmlNodeType.Attribute:
931 if (Prefix.Length == 0)
932 NamespaceURI = string.Empty;
934 NamespaceURI = Reader.LookupNamespace (Prefix, true);
937 case XmlNodeType.Element:
938 case XmlNodeType.EndElement:
939 NamespaceURI = Reader.LookupNamespace (Prefix, true);
946 Prefix = String.Empty;
952 internal class XmlAttributeTokenInfo : XmlTokenInfo
954 public XmlAttributeTokenInfo (XmlTextReader reader)
955 : base (reader, false)
957 NodeType = XmlNodeType.Attribute;
960 public int ValueTokenStartIndex;
961 public int ValueTokenEndIndex;
963 bool cachedNormalization;
964 StringBuilder tmpBuilder = new StringBuilder ();
966 public override string Value {
968 if (cachedNormalization != Reader.Normalization)
970 if (valueCache != null)
973 cachedNormalization = Reader.Normalization;
975 // An empty value should return String.Empty.
976 if (ValueTokenStartIndex == ValueTokenEndIndex) {
977 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
978 if (ti.NodeType == XmlNodeType.EntityReference)
979 valueCache = String.Concat ("&", ti.Name, ";");
981 valueCache = ti.Value;
982 if (cachedNormalization)
987 tmpBuilder.Length = 0;
988 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
989 XmlTokenInfo ti = Reader.attributeValueTokens [i];
990 if (ti.NodeType == XmlNodeType.Text)
991 tmpBuilder.Append (ti.Value);
993 tmpBuilder.Append ('&');
994 tmpBuilder.Append (ti.Name);
995 tmpBuilder.Append (';');
999 valueCache = tmpBuilder.ToString ();
1000 if (cachedNormalization)
1005 set { valueCache = value; }
1008 public override void Clear ()
1012 NodeType = XmlNodeType.Attribute;
1013 ValueTokenStartIndex = ValueTokenEndIndex = 0;
1016 internal override void FillNames ()
1019 if (Prefix == "xmlns" || Name == "xmlns")
1020 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
1023 private void NormalizeSpaces ()
1025 tmpBuilder.Length = 0;
1026 for (int i = 0; i < valueCache.Length; i++)
1027 switch (valueCache [i]) {
1029 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
1034 tmpBuilder.Append (' ');
1037 tmpBuilder.Append (valueCache [i]);
1040 valueCache = tmpBuilder.ToString ();
1044 private XmlTokenInfo cursorToken;
1045 private XmlTokenInfo currentToken;
1046 private XmlAttributeTokenInfo currentAttributeToken;
1047 private XmlTokenInfo currentAttributeValueToken;
1048 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
1049 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
1050 private int currentAttribute;
1051 private int currentAttributeValue;
1052 private int attributeCount;
1054 private XmlParserContext parserContext;
1056 private ReadState readState;
1059 private int elementDepth;
1060 private bool depthUp;
1062 private bool popScope;
1064 private string [] elementNames;
1065 int elementNameStackPos;
1067 private bool allowMultipleRoot;
1069 private bool isStandalone;
1071 private bool returnEntityReference;
1072 private string entityReferenceName;
1074 private char [] nameBuffer;
1075 private int nameLength;
1076 private int nameCapacity;
1077 private const int initialNameCapacity = 32;
1079 private char [] valueBuffer;
1080 private int valueLength;
1081 private int valueCapacity;
1082 private const int initialValueCapacity = 256;
1084 private char [] currentTagBuffer;
1085 private int currentTagLength;
1086 private int currentTagCapacity;
1087 private const int initialCurrentTagCapacity = 256;
1089 private TextReader reader;
1090 private char [] peekChars;
1091 private int peekCharsIndex;
1092 private int peekCharsLength;
1093 private const int peekCharCapacity = 1024;
1098 private int currentLinkedNodeLineNumber;
1099 private int currentLinkedNodeLinePosition;
1100 private bool useProceedingLineInfo;
1102 private XmlNodeType startNodeType;
1103 // State machine attribute.
1104 // XmlDeclaration: after the first node.
1105 // DocumentType: after doctypedecl
1106 // Element: inside document element
1107 // EndElement: after document element
1108 private XmlNodeType currentState;
1110 // For ReadChars()/ReadBase64()/ReadBinHex()
1111 private bool shouldSkipUntilEndTag;
1112 private byte [] base64Cache = new byte [3];
1113 private int base64CacheStartsAt;
1115 // These values are never re-initialized.
1116 private bool namespaces = true;
1117 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
1118 private XmlResolver resolver = new XmlUrlResolver ();
1119 private bool normalization = false;
1121 private bool checkCharacters;
1122 private bool prohibitDtd = false;
1123 private bool closeInput = true;
1124 private EntityHandling entityHandling; // 2.0
1126 private void Init ()
1128 currentToken = new XmlTokenInfo (this, true);
1129 cursorToken = currentToken;
1130 currentAttribute = -1;
1131 currentAttributeValue = -1;
1134 readState = ReadState.Initial;
1135 allowMultipleRoot = false;
1141 popScope = allowMultipleRoot = false;
1142 elementNames = new string [10];
1143 elementNameStackPos = 0;
1145 isStandalone = false;
1146 returnEntityReference = false;
1147 entityReferenceName = String.Empty;
1149 nameBuffer = new char [initialNameCapacity];
1151 nameCapacity = initialNameCapacity;
1153 valueBuffer = new char [initialValueCapacity];
1155 valueCapacity = initialValueCapacity;
1157 currentTagBuffer = new char [initialCurrentTagCapacity];
1158 currentTagLength = 0;
1159 currentTagCapacity = initialCurrentTagCapacity;
1162 peekCharsLength = 0;
1163 if (peekChars == null)
1164 peekChars = new char [peekCharCapacity];
1168 currentTagLength = 0;
1170 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
1171 useProceedingLineInfo = false;
1173 currentState = XmlNodeType.None;
1175 shouldSkipUntilEndTag = false;
1176 base64CacheStartsAt = -1;
1178 checkCharacters = true;
1180 if (Settings != null)
1181 checkCharacters = Settings.CheckCharacters;
1183 prohibitDtd = false;
1185 entityHandling = EntityHandling.ExpandCharEntities;
1188 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1190 startNodeType = fragType;
1191 parserContext = context;
1192 if (context == null) {
1193 XmlNameTable nt = new NameTable ();
1194 parserContext = new XmlParserContext (nt,
1195 new XmlNamespaceManager (nt),
1200 if (url != null && url.Length > 0) {
1203 uri = new Uri (url);
1204 } catch (Exception) {
1205 string path = Path.GetFullPath ("./a");
1206 uri = new Uri (new Uri (path), url);
1208 parserContext.BaseURI = uri.ToString ();
1216 case XmlNodeType.Attribute:
1217 reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1219 case XmlNodeType.Element:
1220 currentState = XmlNodeType.Element;
1221 allowMultipleRoot = true;
1223 case XmlNodeType.Document:
1226 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1232 internal ConformanceLevel Conformance {
1234 if (value == ConformanceLevel.Fragment) {
1235 currentState = XmlNodeType.Element;
1236 allowMultipleRoot = true;
1241 internal void AdjustLineInfoOffset (int lineNumberOffset, int linePositionOffset)
1243 line += lineNumberOffset;
1244 column += linePositionOffset;
1247 internal void SetNameTable (XmlNameTable nameTable)
1249 parserContext.NameTable = nameTable;
1253 // Use this method rather than setting the properties
1254 // directly so that all the necessary properties can
1255 // be changed in harmony with each other. Maybe the
1256 // fields should be in a seperate class to help enforce
1258 private void SetProperties (
1259 XmlNodeType nodeType,
1261 bool isEmptyElement,
1263 bool clearAttributes)
1265 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1266 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1267 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1270 private void SetProperties (
1272 XmlNodeType nodeType,
1274 bool isEmptyElement,
1276 bool clearAttributes)
1279 token.NodeType = nodeType;
1281 token.IsEmptyElement = isEmptyElement;
1282 token.Value = value;
1283 this.elementDepth = depth;
1285 if (clearAttributes)
1291 private void ClearAttributes ()
1293 for (int i = 0; i < attributeCount; i++)
1294 attributeTokens [i].Clear ();
1296 currentAttribute = -1;
1297 currentAttributeValue = -1;
1300 private int PeekChar ()
1302 if (peekCharsLength == peekCharsIndex) {
1303 if (!ReadTextReader ())
1308 char c = peekChars [peekCharsIndex];
1309 if (c != 0) return c;
1314 private int ReadChar ()
1318 if (peekCharsLength == peekCharsIndex) {
1319 if (!ReadTextReader ())
1323 ch = peekChars [peekCharsIndex++];
1328 } else if (ch == 0) {
1333 if (currentState != XmlNodeType.Element)
1334 AppendCurrentTagChar (ch);
1338 private bool ReadTextReader ()
1341 peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity);
1342 if (peekCharsLength == 0)
1347 private string ExpandSurrogateChar (int ch)
1349 if (ch < Char.MaxValue)
1350 return ((char) ch).ToString ();
1352 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1353 return new string (tmp);
1357 // This should really keep track of some state so
1358 // that it's not possible to have more than one document
1359 // element or text outside of the document element.
1360 private bool ReadContent ()
1362 currentTagLength = 0;
1364 parserContext.NamespaceManager.PopScope ();
1368 if (returnEntityReference)
1369 SetEntityReferenceProperties ();
1371 int c = PeekChar ();
1373 readState = ReadState.EndOfFile;
1374 ClearValueBuffer ();
1376 XmlNodeType.None, // nodeType
1377 String.Empty, // name
1378 false, // isEmptyElement
1380 true // clearAttributes
1383 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1392 case '\r': goto case ' ';
1393 case '\n': goto case ' ';
1394 case '\t': goto case ' ';
1396 if (whitespaceHandling == WhitespaceHandling.All ||
1397 whitespaceHandling == WhitespaceHandling.Significant)
1401 return ReadContent ();
1410 return this.ReadState != ReadState.EndOfFile;
1413 private void SetEntityReferenceProperties ()
1415 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1416 if (this.isStandalone)
1417 if (DTD == null || decl == null || !decl.IsInternalSubset)
1418 throw new XmlException (this as IXmlLineInfo,
1419 "Standalone document must not contain any references to an non-internally declared entity.");
1420 if (decl != null && decl.NotationName != null)
1421 throw new XmlException (this as IXmlLineInfo,
1422 "Reference to any unparsed entities is not allowed here.");
1424 ClearValueBuffer ();
1426 XmlNodeType.EntityReference, // nodeType
1427 entityReferenceName, // name
1428 false, // isEmptyElement
1430 true // clearAttributes
1433 returnEntityReference = false;
1434 entityReferenceName = String.Empty;
1437 // The leading '<' has already been consumed.
1438 private void ReadTag ()
1440 switch (PeekChar ())
1448 ReadProcessingInstruction ();
1460 // The leading '<' has already been consumed.
1461 private void ReadStartTag ()
1463 if (currentState == XmlNodeType.EndElement)
1464 throw new XmlException (this as IXmlLineInfo,
1465 "Multiple document element was detected.");
1466 currentState = XmlNodeType.Element;
1468 parserContext.NamespaceManager.PushScope ();
1470 currentLinkedNodeLineNumber = line;
1471 currentLinkedNodeLinePosition = column;
1473 string name = ReadName ();
1474 if (currentState == XmlNodeType.EndElement)
1475 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1477 bool isEmptyElement = false;
1482 if (XmlChar.IsFirstNameChar (PeekChar ()))
1483 ReadAttributes (false);
1484 cursorToken = this.currentToken;
1487 for (int i = 0; i < attributeCount; i++)
1488 attributeTokens [i].FillNames ();
1491 for (int i = 0; i < attributeCount; i++) {
1492 for (int j = i + 1; j < attributeCount; j++)
1493 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1494 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1495 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1496 throw new XmlException (this as IXmlLineInfo,
1497 "Attribute name and qualified name must be identical.");
1500 string baseUri = GetAttribute ("xml:base");
1501 if (baseUri != null) {
1502 if (this.resolver != null)
1503 parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1505 parserContext.BaseURI = baseUri;
1507 string xmlLang = GetAttribute ("xml:lang");
1508 if (xmlLang != null)
1509 parserContext.XmlLang = xmlLang;
1510 string xmlSpaceAttr = GetAttribute ("xml:space");
1511 if (xmlSpaceAttr != null) {
1512 if (xmlSpaceAttr == "preserve")
1513 parserContext.XmlSpace = XmlSpace.Preserve;
1514 else if (xmlSpaceAttr == "default")
1515 parserContext.XmlSpace = XmlSpace.Default;
1517 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1519 if (PeekChar () == '/') {
1521 isEmptyElement = true;
1526 PushElementName (name);
1527 parserContext.PushScope ();
1532 XmlNodeType.Element, // nodeType
1534 isEmptyElement, // isEmptyElement
1536 false // clearAttributes
1539 if (LookupNamespace (Prefix) == null)
1540 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1542 for (int i = 0; i < attributeCount; i++) {
1543 MoveToAttribute (i);
1544 if (LookupNamespace (Prefix) == null)
1545 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1552 CheckCurrentStateUpdate ();
1555 private void PushElementName (string name)
1557 if (elementNames.Length == elementNameStackPos) {
1558 string [] newArray = new string [elementNames.Length * 2];
1559 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1560 elementNames = newArray;
1562 elementNames [elementNameStackPos++] = name;
1565 // The reader is positioned on the first character
1566 // of the element's name.
1567 private void ReadEndTag ()
1569 if (currentState != XmlNodeType.Element)
1570 throw new XmlException (this as IXmlLineInfo,
1571 "End tag cannot appear in this state.");
1573 currentLinkedNodeLineNumber = line;
1574 currentLinkedNodeLinePosition = column;
1576 string name = ReadName ();
1577 if (elementNameStackPos == 0)
1578 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1579 string expected = elementNames [--elementNameStackPos];
1580 if (expected != name)
1581 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1582 parserContext.PopScope ();
1584 ExpectAfterWhitespace ('>');
1589 XmlNodeType.EndElement, // nodeType
1591 false, // isEmptyElement
1593 true // clearAttributes
1598 CheckCurrentStateUpdate ();
1601 private void CheckCurrentStateUpdate ()
1603 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1604 currentState = XmlNodeType.EndElement;
1607 private void AppendNameChar (int ch)
1609 if (nameLength == nameCapacity)
1610 ExpandNameCapacity ();
1611 if (ch < Char.MaxValue)
1612 nameBuffer [nameLength++] = (char) ch;
1614 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1615 if (nameLength == nameCapacity)
1616 ExpandNameCapacity ();
1617 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1621 private void ExpandNameCapacity ()
1623 nameCapacity = nameCapacity * 2;
1624 char [] oldNameBuffer = nameBuffer;
1625 nameBuffer = new char [nameCapacity];
1626 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1629 private string CreateNameString ()
1631 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1634 private void AppendValueChar (int ch)
1636 if (valueLength == valueCapacity)
1637 ExpandValueCapacity ();
1638 if (ch < Char.MaxValue)
1639 valueBuffer [valueLength++] = (char) ch;
1641 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1642 if (valueLength == valueCapacity)
1643 ExpandValueCapacity ();
1644 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1648 private void ExpandValueCapacity ()
1650 valueCapacity = valueCapacity * 2;
1651 char [] oldValueBuffer = valueBuffer;
1652 valueBuffer = new char [valueCapacity];
1653 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1656 private string CreateValueString ()
1658 return new string (valueBuffer, 0, valueLength);
1661 private void ClearValueBuffer ()
1666 private void AppendCurrentTagChar (int ch)
1668 if (currentTagLength == currentTagCapacity)
1669 ExpandCurrentTagCapacity ();
1670 if (ch < Char.MaxValue)
1671 currentTagBuffer [currentTagLength++] = (char) ch;
1673 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1674 if (currentTagLength == currentTagCapacity)
1675 ExpandCurrentTagCapacity ();
1676 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1680 private void ExpandCurrentTagCapacity ()
1682 currentTagCapacity = currentTagCapacity * 2;
1683 char [] oldCurrentTagBuffer = currentTagBuffer;
1684 currentTagBuffer = new char [currentTagCapacity];
1685 Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1688 private string CreateCurrentTagString ()
1690 return new string (currentTagBuffer, 0, currentTagLength);
1693 private void ClearCurrentTagBuffer ()
1695 currentTagLength = 0;
1698 // The reader is positioned on the first character
1700 private void ReadText (bool notWhitespace)
1702 if (currentState != XmlNodeType.Element)
1703 throw new XmlException (this as IXmlLineInfo,
1704 "Text node cannot appear in this state.");
1707 ClearValueBuffer ();
1709 int ch = PeekChar ();
1710 bool previousWasCloseBracket = false;
1712 while (ch != '<' && ch != -1) {
1715 ch = ReadReference (false);
1716 if (returnEntityReference) // Returns -1 if char validation should not be done
1718 } else if (normalization && ch == '\r') {
1722 // append '\n' instead of '\r'.
1723 AppendValueChar ('\n');
1724 // and in case of "\r\n", discard '\r'.
1726 if (XmlChar.IsInvalid (ch))
1727 throw new XmlException (this, "Not allowed character was found.");
1731 AppendValueChar (ch);
1735 if (previousWasCloseBracket)
1736 if (PeekChar () == '>')
1737 throw new XmlException (this as IXmlLineInfo,
1738 "Inside text content, character sequence ']]>' is not allowed.");
1739 previousWasCloseBracket = true;
1741 else if (previousWasCloseBracket)
1742 previousWasCloseBracket = false;
1744 notWhitespace = true;
1747 if (returnEntityReference && valueLength == 0) {
1748 SetEntityReferenceProperties ();
1750 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1751 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1753 nodeType, // nodeType
1754 String.Empty, // name
1755 false, // isEmptyElement
1756 null, // value: create only when required
1757 true // clearAttributes
1762 // The leading '&' has already been consumed.
1763 // Returns true if the entity reference isn't a simple
1764 // character reference or one of the predefined entities.
1765 // This allows the ReadText method to break so that the
1766 // next call to Read will return the EntityReference node.
1767 private int ReadReference (bool ignoreEntityReferences)
1769 if (PeekChar () == '#') {
1771 return ReadCharacterReference ();
1773 return ReadEntityReference (ignoreEntityReferences);
1776 private int ReadCharacterReference ()
1780 if (PeekChar () == 'x') {
1783 while (PeekChar () != ';' && PeekChar () != -1) {
1784 int ch = ReadChar ();
1786 if (ch >= '0' && ch <= '9')
1787 value = (value << 4) + ch - '0';
1788 else if (ch >= 'A' && ch <= 'F')
1789 value = (value << 4) + ch - 'A' + 10;
1790 else if (ch >= 'a' && ch <= 'f')
1791 value = (value << 4) + ch - 'a' + 10;
1793 throw new XmlException (this as IXmlLineInfo,
1794 String.Format (CultureInfo.InvariantCulture,
1795 "invalid hexadecimal digit: {0} (#x{1:X})",
1800 while (PeekChar () != ';' && PeekChar () != -1) {
1801 int ch = ReadChar ();
1803 if (ch >= '0' && ch <= '9')
1804 value = value * 10 + ch - '0';
1806 throw new XmlException (this as IXmlLineInfo,
1807 String.Format (CultureInfo.InvariantCulture,
1808 "invalid decimal digit: {0} (#x{1:X})",
1816 // There is no way to save surrogate pairs...
1817 if (CharacterChecking && XmlChar.IsInvalid (value))
1818 throw new XmlException (this as IXmlLineInfo,
1819 "Referenced character was not allowed in XML. Normalization is " + normalization + ", checkCharacters = " + checkCharacters);
1823 // Returns -1 if it should not be validated.
1824 // Real EOF must not be detected here.
1825 private int ReadEntityReference (bool ignoreEntityReferences)
1827 string name = ReadName ();
1830 int predefined = XmlChar.GetPredefinedEntity (name);
1831 if (predefined >= 0)
1834 if (ignoreEntityReferences) {
1835 AppendValueChar ('&');
1836 for (int i = 0; i < name.Length; i++)
1837 AppendValueChar (name [i]);
1838 AppendValueChar (';');
1840 returnEntityReference = true;
1841 entityReferenceName = name;
1847 // The reader is positioned on the first character of
1848 // the attribute name.
1849 private void ReadAttributes (bool isXmlDecl)
1852 bool requireWhitespace = false;
1853 currentAttribute = -1;
1854 currentAttributeValue = -1;
1857 if (!SkipWhitespace () && requireWhitespace)
1858 throw new XmlException ("Unexpected token. Name is required here.");
1860 IncrementAttributeToken ();
1861 currentAttributeToken.LineNumber = line;
1862 currentAttributeToken.LinePosition = column;
1864 currentAttributeToken.LocalName =
1865 currentAttributeToken.Name = ReadName ();
1866 ExpectAfterWhitespace ('=');
1868 ReadAttributeValueTokens (-1);
1871 if (currentAttributeToken.Name == "xmlns")
1872 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1873 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1874 string nsPrefix = currentAttributeToken.Name.Substring (6);
1875 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1878 if (!SkipWhitespace ())
1879 requireWhitespace = true;
1880 peekChar = PeekChar ();
1882 if (peekChar == '?')
1885 else if (peekChar == '/' || peekChar == '>')
1887 } while (peekChar != -1);
1889 currentAttribute = -1;
1890 currentAttributeValue = -1;
1893 private void AddAttribute (string name, string value)
1895 IncrementAttributeToken ();
1896 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1897 ati.Name = "SYSTEM";
1899 IncrementAttributeValueToken ();
1900 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1902 SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1906 private void IncrementAttributeToken ()
1909 if (attributeTokens.Length == currentAttribute) {
1910 XmlAttributeTokenInfo [] newArray =
1911 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1912 attributeTokens.CopyTo (newArray, 0);
1913 attributeTokens = newArray;
1915 if (attributeTokens [currentAttribute] == null)
1916 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1917 currentAttributeToken = attributeTokens [currentAttribute];
1918 currentAttributeToken.Clear ();
1921 private void IncrementAttributeValueToken ()
1923 ClearValueBuffer ();
1924 currentAttributeValue++;
1925 if (attributeValueTokens.Length == currentAttributeValue) {
1926 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1927 attributeValueTokens.CopyTo (newArray, 0);
1928 attributeValueTokens = newArray;
1930 if (attributeValueTokens [currentAttributeValue] == null)
1931 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1932 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1933 currentAttributeValueToken.Clear ();
1936 // LAMESPEC: Orthodox XML reader should normalize attribute values
1937 private void ReadAttributeValueTokens (int dummyQuoteChar)
1939 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1941 if (quoteChar != '\'' && quoteChar != '\"')
1942 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1943 currentAttributeToken.QuoteChar = (char) quoteChar;
1945 IncrementAttributeValueToken ();
1946 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1947 currentAttributeValueToken.LineNumber = line;
1948 currentAttributeValueToken.LinePosition = column;
1950 bool incrementToken = false;
1951 bool isNewToken = true;
1956 if (ch == quoteChar)
1959 if (incrementToken) {
1960 IncrementAttributeValueToken ();
1961 currentAttributeValueToken.LineNumber = line;
1962 currentAttributeValueToken.LinePosition = column;
1963 incrementToken = false;
1970 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1972 if (dummyQuoteChar < 0)
1973 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1974 else // Attribute value constructor.
1978 int startPosition = currentTagLength - 1;
1979 if (PeekChar () == '#') {
1981 ch = ReadCharacterReference ();
1982 if (CharacterChecking && XmlChar.IsInvalid (ch))
1983 throw new XmlException (this as IXmlLineInfo,
1984 "Not allowed character was found.");
1985 AppendValueChar (ch);
1988 // Check XML 1.0 section 3.1 WFC.
1989 string entName = ReadName ();
1991 int predefined = XmlChar.GetPredefinedEntity (entName);
1992 if (predefined < 0) {
1993 CheckAttributeEntityReferenceWFC (entName);
1994 currentAttributeValueToken.Value = CreateValueString ();
1995 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1997 IncrementAttributeValueToken ();
1998 currentAttributeValueToken.Name = entName;
1999 currentAttributeValueToken.Value = String.Empty;
2000 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
2001 incrementToken = true;
2004 AppendValueChar (predefined);
2007 if (CharacterChecking && XmlChar.IsInvalid (ch))
2008 throw new XmlException (this, "Invalid character was found.");
2009 AppendValueChar (ch);
2015 if (!incrementToken) {
2016 currentAttributeValueToken.Value = CreateValueString ();
2017 currentAttributeValueToken.NodeType = XmlNodeType.Text;
2019 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
2023 private void CheckAttributeEntityReferenceWFC (string entName)
2025 DTDEntityDeclaration entDecl =
2026 DTD == null ? null : DTD.EntityDecls [entName];
2027 if (DTD != null && resolver != null && entDecl == null)
2028 throw new XmlException (this, "Referenced entity does not exist.");
2030 if (entDecl == null)
2033 if (entDecl.HasExternalReference)
2034 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
2035 if (isStandalone && !entDecl.IsInternalSubset)
2036 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
2037 if (entDecl.EntityValue.IndexOf ('<') >= 0)
2038 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
2041 // The reader is positioned on the first character
2044 // It may be xml declaration or processing instruction.
2045 private void ReadProcessingInstruction ()
2047 string target = ReadName ();
2048 if (target == "xml") {
2049 ReadXmlDeclaration ();
2051 } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml")
2052 throw new XmlException (this as IXmlLineInfo,
2053 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
2055 if (currentState == XmlNodeType.None)
2056 currentState = XmlNodeType.XmlDeclaration;
2058 if (!SkipWhitespace ())
2059 if (PeekChar () != '?')
2060 throw new XmlException (this as IXmlLineInfo,
2061 "Invalid processing instruction name was found.");
2063 ClearValueBuffer ();
2065 while (PeekChar () != -1) {
2066 int ch = ReadChar ();
2068 if (ch == '?' && PeekChar () == '>') {
2073 if (CharacterChecking && XmlChar.IsInvalid (ch))
2074 throw new XmlException (this, "Invalid character was found.");
2075 AppendValueChar (ch);
2079 XmlNodeType.ProcessingInstruction, // nodeType
2081 false, // isEmptyElement
2082 null, // value: create only when required
2083 true // clearAttributes
2087 // The reader is positioned after "<?xml "
2088 private void ReadXmlDeclaration ()
2090 if (currentState != XmlNodeType.None) {
2091 throw new XmlException (this as IXmlLineInfo,
2092 "XML declaration cannot appear in this state.");
2094 currentState = XmlNodeType.XmlDeclaration;
2098 ReadAttributes (true); // They must have "version."
2099 string version = GetAttribute ("version");
2101 string message = null;
2103 if (attributeTokens [0].Name != "version" || version != "1.0")
2104 message = "Version 1.0 declaration is required in XML Declaration.";
2105 else if (attributeCount > 1 &&
2106 (attributeTokens [1].Name != "encoding" &&
2107 attributeTokens [1].Name != "standalone"))
2108 message = "Invalid Xml Declaration markup was found.";
2109 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
2110 message = "Invalid Xml Declaration markup was found.";
2111 string sa = GetAttribute ("standalone");
2112 if (sa != null && sa != "yes" && sa != "no")
2113 message = "Only 'yes' or 'no' is allowed for standalone.";
2115 this.isStandalone = (sa == "yes");
2117 if (message != null)
2118 throw new XmlException (this as IXmlLineInfo, message);
2121 XmlNodeType.XmlDeclaration, // nodeType
2123 false, // isEmptyElement
2124 new string (currentTagBuffer, 6, currentTagLength - 6), // value
2125 false // clearAttributes
2131 internal void SkipTextDeclaration ()
2133 this.currentState = XmlNodeType.Element;
2135 if (PeekChar () != '<')
2140 if (PeekChar () != '?') {
2146 while (peekCharsIndex < 6) {
2147 if (PeekChar () < 0)
2152 if (new string (peekChars, 2, 4) != "xml ") {
2153 if (new string (peekChars, 2, 3).ToLower (CultureInfo.InvariantCulture) == "xml") {
2154 throw new XmlException (this as IXmlLineInfo,
2155 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
2164 if (PeekChar () == 'v') {
2166 ExpectAfterWhitespace ('=');
2168 int quoteChar = ReadChar ();
2169 char [] expect1_0 = new char [3];
2170 int versionLength = 0;
2171 switch (quoteChar) {
2174 while (PeekChar () != quoteChar) {
2175 if (PeekChar () == -1)
2176 throw new XmlException (this as IXmlLineInfo,
2177 "Invalid version declaration inside text declaration.");
2178 else if (versionLength == 3)
2179 throw new XmlException (this as IXmlLineInfo,
2180 "Invalid version number inside text declaration.");
2182 expect1_0 [versionLength] = (char) ReadChar ();
2184 if (versionLength == 3 && new String (expect1_0) != "1.0")
2185 throw new XmlException (this as IXmlLineInfo,
2186 "Invalid version number inside text declaration.");
2193 throw new XmlException (this as IXmlLineInfo,
2194 "Invalid version declaration inside text declaration.");
2198 if (PeekChar () == 'e') {
2199 Expect ("encoding");
2200 ExpectAfterWhitespace ('=');
2202 int quoteChar = ReadChar ();
2203 switch (quoteChar) {
2206 while (PeekChar () != quoteChar)
2207 if (ReadChar () == -1)
2208 throw new XmlException (this as IXmlLineInfo,
2209 "Invalid encoding declaration inside text declaration.");
2214 throw new XmlException (this as IXmlLineInfo,
2215 "Invalid encoding declaration inside text declaration.");
2217 // Encoding value should be checked inside XmlInputStream.
2220 throw new XmlException (this as IXmlLineInfo,
2221 "Encoding declaration is mandatory in text declaration.");
2226 // The reader is positioned on the first character after
2227 // the leading '<!'.
2228 private void ReadDeclaration ()
2230 int ch = PeekChar ();
2248 throw new XmlException (this as IXmlLineInfo,
2249 "Unexpected declaration markup was found.");
2253 // The reader is positioned on the first character after
2254 // the leading '<!--'.
2255 private void ReadComment ()
2257 if (currentState == XmlNodeType.None)
2258 currentState = XmlNodeType.XmlDeclaration;
2260 ClearValueBuffer ();
2262 while (PeekChar () != -1) {
2263 int ch = ReadChar ();
2265 if (ch == '-' && PeekChar () == '-') {
2268 if (PeekChar () != '>')
2269 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2275 if (XmlChar.IsInvalid (ch))
2276 throw new XmlException (this as IXmlLineInfo,
2277 "Not allowed character was found.");
2279 AppendValueChar (ch);
2283 XmlNodeType.Comment, // nodeType
2284 String.Empty, // name
2285 false, // isEmptyElement
2286 null, // value: create only when required
2287 true // clearAttributes
2291 // The reader is positioned on the first character after
2292 // the leading '<![CDATA['.
2293 private void ReadCDATA ()
2295 if (currentState != XmlNodeType.Element)
2296 throw new XmlException (this as IXmlLineInfo,
2297 "CDATA section cannot appear in this state.");
2299 ClearValueBuffer ();
2303 while (PeekChar () != -1) {
2308 if (ch == ']' && PeekChar () == ']') {
2309 ch = ReadChar (); // ']'
2311 if (PeekChar () == '>') {
2318 if (normalization && ch == '\r') {
2321 // append '\n' instead of '\r'.
2322 AppendValueChar ('\n');
2323 // otherwise, discard '\r'.
2326 if (CharacterChecking && XmlChar.IsInvalid (ch))
2327 throw new XmlException (this, "Invalid character was found.");
2329 AppendValueChar (ch);
2333 XmlNodeType.CDATA, // nodeType
2334 String.Empty, // name
2335 false, // isEmptyElement
2336 null, // value: create only when required
2337 true // clearAttributes
2341 // The reader is positioned on the first character after
2342 // the leading '<!DOCTYPE'.
2343 private void ReadDoctypeDecl ()
2346 throw new XmlException (this as IXmlLineInfo,
2347 "Document Type Declaration (DTD) is prohibited in this XML.");
2348 switch (currentState) {
2349 case XmlNodeType.DocumentType:
2350 case XmlNodeType.Element:
2351 case XmlNodeType.EndElement:
2352 throw new XmlException (this as IXmlLineInfo,
2353 "Document type cannot appear in this state.");
2355 currentState = XmlNodeType.DocumentType;
2357 string doctypeName = null;
2358 string publicId = null;
2359 string systemId = null;
2360 int intSubsetStartLine = 0;
2361 int intSubsetStartColumn = 0;
2364 doctypeName = ReadName ();
2369 systemId = ReadSystemLiteral (true);
2372 publicId = ReadPubidLiteral ();
2373 if (!SkipWhitespace ())
2374 throw new XmlException (this as IXmlLineInfo,
2375 "Whitespace is required between PUBLIC id and SYSTEM id.");
2376 systemId = ReadSystemLiteral (false);
2382 if(PeekChar () == '[')
2384 // read markupdecl etc. or end of decl
2386 intSubsetStartLine = this.LineNumber;
2387 intSubsetStartColumn = this.LinePosition;
2388 int startPos = currentTagLength;
2389 ReadInternalSubset ();
2390 int endPos = currentTagLength - 1;
2391 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2393 // end of DOCTYPE decl.
2394 ExpectAfterWhitespace ('>');
2396 GenerateDTDObjectModel (doctypeName, publicId,
2397 systemId, parserContext.InternalSubset,
2398 intSubsetStartLine, intSubsetStartColumn);
2400 // set properties for <!DOCTYPE> node
2402 XmlNodeType.DocumentType, // nodeType
2403 doctypeName, // name
2404 false, // isEmptyElement
2405 parserContext.InternalSubset, // value
2406 true // clearAttributes
2409 if (publicId != null)
2410 AddAttribute ("PUBLIC", publicId);
2411 if (systemId != null)
2412 AddAttribute ("SYSTEM", systemId);
2413 currentAttribute = currentAttributeValue = -1;
2416 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2417 string systemId, string internalSubset)
2419 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2422 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2423 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2426 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2427 DTD.BaseURI = BaseURI;
2429 DTD.PublicId = publicId;
2430 DTD.SystemId = systemId;
2431 DTD.InternalSubset = internalSubset;
2432 DTD.XmlResolver = resolver;
2433 DTD.IsStandalone = isStandalone;
2434 DTD.LineNumber = line;
2435 DTD.LinePosition = column;
2437 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2438 dr.Normalization = this.normalization;
2439 #if DTD_HANDLE_EVENTS
2440 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2442 return dr.GenerateDTDObjectModel ();
2445 private void OnValidationEvent (object o, ValidationEventArgs e)
2447 #if DTD_HANDLE_EVENTS
2448 if (ValidationEventHandler != null)
2449 // Override object as this.
2450 ValidationEventHandler (this, e);
2454 private enum DtdInputState
2467 private class DtdInputStateStack
2469 Stack intern = new Stack ();
2470 public DtdInputStateStack ()
2472 Push (DtdInputState.Free);
2475 public DtdInputState Peek ()
2477 return (DtdInputState) intern.Peek ();
2480 public DtdInputState Pop ()
2482 return (DtdInputState) intern.Pop ();
2485 public void Push (DtdInputState val)
2492 DtdInputStateStack stateStack = new DtdInputStateStack ();
2493 DtdInputState State {
2494 get { return stateStack.Peek (); }
2497 // Simply read but not generate any result.
2498 private void ReadInternalSubset ()
2500 bool continueParse = true;
2502 while (continueParse) {
2503 switch (ReadChar ()) {
2506 case DtdInputState.Free:
2507 continueParse = false;
2509 case DtdInputState.InsideDoubleQuoted:
2511 case DtdInputState.InsideSingleQuoted:
2514 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2518 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2521 case DtdInputState.InsideDoubleQuoted:
2522 case DtdInputState.InsideSingleQuoted:
2523 case DtdInputState.Comment:
2524 continue; // well-formed
2526 int c = ReadChar ();
2529 stateStack.Push (DtdInputState.PI);
2532 switch (ReadChar ()) {
2534 switch (ReadChar ()) {
2537 stateStack.Push (DtdInputState.ElementDecl);
2541 stateStack.Push (DtdInputState.EntityDecl);
2544 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2549 stateStack.Push (DtdInputState.AttlistDecl);
2553 stateStack.Push (DtdInputState.NotationDecl);
2557 stateStack.Push (DtdInputState.Comment);
2562 throw new XmlException (this as IXmlLineInfo, String.Format ("unexpected '<{0}'.", (char) c));
2566 if (State == DtdInputState.InsideSingleQuoted)
2568 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2569 stateStack.Push (DtdInputState.InsideSingleQuoted);
2572 if (State == DtdInputState.InsideDoubleQuoted)
2574 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2575 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2579 case DtdInputState.ElementDecl:
2580 goto case DtdInputState.NotationDecl;
2581 case DtdInputState.AttlistDecl:
2582 goto case DtdInputState.NotationDecl;
2583 case DtdInputState.EntityDecl:
2584 goto case DtdInputState.NotationDecl;
2585 case DtdInputState.NotationDecl:
2588 case DtdInputState.InsideDoubleQuoted:
2589 case DtdInputState.InsideSingleQuoted:
2590 case DtdInputState.Comment:
2593 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2597 if (State == DtdInputState.PI) {
2598 if (ReadChar () == '>')
2603 if (State == DtdInputState.Comment) {
2604 if (PeekChar () == '-') {
2612 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2613 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2619 // The reader is positioned on the first 'S' of "SYSTEM".
2620 private string ReadSystemLiteral (bool expectSYSTEM)
2624 if (!SkipWhitespace ())
2625 throw new XmlException (this as IXmlLineInfo,
2626 "Whitespace is required after 'SYSTEM'.");
2630 int quoteChar = ReadChar (); // apos or quot
2631 int startPos = currentTagLength;
2633 ClearValueBuffer ();
2634 while (c != quoteChar) {
2637 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2639 AppendValueChar (c);
2641 return CreateValueString ();
2644 private string ReadPubidLiteral()
2647 if (!SkipWhitespace ())
2648 throw new XmlException (this as IXmlLineInfo,
2649 "Whitespace is required after 'PUBLIC'.");
2650 int quoteChar = ReadChar ();
2651 int startPos = currentTagLength;
2653 ClearValueBuffer ();
2654 while(c != quoteChar)
2657 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2658 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2659 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2661 AppendValueChar (c);
2663 return CreateValueString ();
2666 // The reader is positioned on the first character
2668 private string ReadName ()
2670 int ch = PeekChar ();
2671 if (!XmlChar.IsFirstNameChar (ch))
2672 throw new XmlException (this as IXmlLineInfo,String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
2676 AppendNameChar (ReadChar ());
2678 while (XmlChar.IsNameChar (PeekChar ())) {
2679 AppendNameChar (ReadChar ());
2682 return CreateNameString ();
2685 // Read the next character and compare it against the
2686 // specified character.
2687 private void Expect (int expected)
2689 int ch = ReadChar ();
2691 if (ch != expected) {
2692 throw new XmlException (this as IXmlLineInfo,
2693 String.Format (CultureInfo.InvariantCulture,
2694 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2702 private void Expect (string expected)
2704 int len = expected.Length;
2705 for(int i=0; i< len; i++)
2706 Expect (expected[i]);
2709 private void ExpectAfterWhitespace (char c)
2712 int i = ReadChar ();
2713 if (i < 0x21 && XmlChar.IsWhitespace (i))
2716 throw new XmlException (this, String.Format (CultureInfo.InvariantCulture, "Expected {0}, but found {1} [{2}]", c, (char) i, i));
2721 // Does not consume the first non-whitespace character.
2722 private bool SkipWhitespace ()
2724 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2727 while (XmlChar.IsWhitespace (PeekChar ()))
2732 private void ReadWhitespace ()
2734 if (currentState == XmlNodeType.None)
2735 currentState = XmlNodeType.XmlDeclaration;
2737 ClearValueBuffer ();
2738 int ch = PeekChar ();
2740 AppendValueChar (ReadChar ());
2741 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2743 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2746 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2747 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2748 SetProperties (nodeType,
2751 null, // value: create only when required
2758 // Since ReadBase64() is processed for every 4 chars, it does
2759 // not handle '=' here.
2760 private byte GetBase64Byte (char ch)
2768 if (ch >= 'A' && ch <= 'Z')
2769 return (byte) (ch - 'A');
2770 else if (ch >= 'a' && ch <= 'z')
2771 return (byte) (ch - 'a' + 26);
2772 else if (ch >= '0' && ch <= '9')
2773 return (byte) (ch - '0' + 52);
2775 throw new XmlException ("Invalid Base64 character was found.");
2779 // Returns -1 if it should throw an error.
2780 private int ReadCharsInternal (char [] buffer, int offset, int length)
2782 if (IsEmptyElement) {
2788 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2789 else if (length < 0)
2790 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2791 else if (buffer.Length < offset + length)
2792 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2794 if (NodeType != XmlNodeType.Element)
2797 shouldSkipUntilEndTag = true;
2799 int bufIndex = offset;
2800 for (int i = 0; i < length; i++) {
2801 int c = PeekChar ();
2804 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2807 if (PeekChar () != '/') {
2808 buffer [bufIndex++] = '<';
2811 // Seems to skip immediate EndElement
2818 shouldSkipUntilEndTag = false;
2819 Read (); // move to the next node
2823 if (c < Char.MaxValue)
2824 buffer [bufIndex++] = (char) c;
2826 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2827 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2835 private bool ReadUntilEndTag ()
2842 throw new XmlException (this as IXmlLineInfo,
2843 "Unexpected end of xml.");
2845 if (PeekChar () != '/')
2848 string name = ReadName ();
2849 if (name != elementNames [elementNameStackPos - 1])
2853 elementNames [--elementNameStackPos] = null;