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/
14 // Some thought needs to be given to performance.
16 // If current node is on an Attribute, Prefix might be null, and
17 // in several fields which uses XmlReader, it should be considered.
21 using System.Collections;
23 using System.Security.Policy;
25 using System.Xml.Schema;
27 using Mono.Xml.Native;
31 public class XmlTextReader : XmlReader, IXmlLineInfo
35 protected XmlTextReader ()
39 public XmlTextReader (Stream input)
40 : this (new XmlStreamReader (input))
44 public XmlTextReader (string url)
45 : this(url, new NameTable ())
49 public XmlTextReader (TextReader input)
50 : this (input, new NameTable ())
54 protected XmlTextReader (XmlNameTable nt)
55 : this (String.Empty, null, XmlNodeType.None, null)
59 public XmlTextReader (Stream input, XmlNameTable nt)
60 : this(new XmlStreamReader (input), nt)
64 public XmlTextReader (string url, Stream input)
65 : this (url, new XmlStreamReader (input))
69 public XmlTextReader (string url, TextReader input)
70 : this (url, input, new NameTable ())
74 public XmlTextReader (string url, XmlNameTable nt)
76 Uri uri = resolver.ResolveUri (null, url);
77 Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
78 XmlParserContext ctx = new XmlParserContext (nt,
79 new XmlNamespaceManager (nt),
82 this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document);
85 public XmlTextReader (TextReader input, XmlNameTable nt)
86 : this (String.Empty, input, nt)
90 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
91 : this (context != null ? context.BaseURI : String.Empty,
92 new XmlStreamReader (xmlFragment),
98 public XmlTextReader (string url, Stream input, XmlNameTable nt)
99 : this (url, new XmlStreamReader (input), nt)
103 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
104 : this (url, input, XmlNodeType.Document, null)
108 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
109 : this (context != null ? context.BaseURI : String.Empty,
110 new StringReader (xmlFragment),
116 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
118 InitializeContext (url, context, fragment, fragType);
125 public override int AttributeCount
127 get { return attributeCount; }
130 public override string BaseURI
132 get { return parserContext.BaseURI; }
135 public override int Depth
138 int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element ? 0 : -1;
139 if (currentAttributeValue >= 0)
140 return nodeTypeMod + elementDepth + 2; // inside attribute value.
141 else if (currentAttribute >= 0)
142 return nodeTypeMod + elementDepth + 1;
147 public Encoding Encoding
149 get { return parserContext.Encoding; }
153 public EntityHandling EntityHandling {
154 get { throw new NotImplementedException (); }
158 public override bool EOF
163 readState == ReadState.EndOfFile ||
164 readState == ReadState.Closed;
170 public override Evidence [] Evidences {
171 get { return base.Evidences; }
175 public override bool HasValue {
176 get { return cursorToken.Value != null; }
179 public override bool IsDefault {
180 // XmlTextReader does not expand default attributes.
181 get { return false; }
184 public override bool IsEmptyElement {
185 get { return cursorToken.IsEmptyElement; }
188 public override string this [int i] {
189 get { return GetAttribute (i); }
192 public override string this [string name] {
193 get { return GetAttribute (name); }
196 public override string this [string localName, string namespaceName] {
197 get { return GetAttribute (localName, namespaceName); }
200 public int LineNumber {
202 if (useProceedingLineInfo)
205 return cursorToken.LineNumber;
209 public int LinePosition {
211 if (useProceedingLineInfo)
214 return cursorToken.LinePosition;
218 public override string LocalName {
219 get { return cursorToken.LocalName; }
222 public override string Name {
223 get { return cursorToken.Name; }
226 public bool Namespaces {
227 get { return namespaces; }
229 if (readState != ReadState.Initial)
230 throw new InvalidOperationException ("Namespaces have to be set before reading.");
235 public override string NamespaceURI {
236 get { return cursorToken.NamespaceURI; }
239 public override XmlNameTable NameTable {
240 get { return parserContext.NameTable; }
243 public override XmlNodeType NodeType {
244 get { return cursorToken.NodeType; }
247 public bool Normalization {
248 get { return normalization; }
249 set { normalization = value; }
252 public override string Prefix {
253 get { return cursorToken.Prefix; }
256 public override char QuoteChar {
257 get { return cursorToken.QuoteChar; }
260 public override ReadState ReadState {
261 get { return readState; }
264 public override string Value {
265 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
268 public WhitespaceHandling WhitespaceHandling {
269 get { return whitespaceHandling; }
270 set { whitespaceHandling = value; }
273 public override string XmlLang {
274 get { return parserContext.XmlLang; }
277 public XmlResolver XmlResolver {
278 set { resolver = value; }
281 public override XmlSpace XmlSpace {
282 get { return parserContext.XmlSpace; }
289 public override void Close ()
291 readState = ReadState.Closed;
293 cursorToken.Clear ();
294 currentToken.Clear ();
300 public override string GetAttribute (int i)
302 if (i >= attributeCount)
303 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
305 return attributeTokens [i].Value;
309 // MS.NET 1.0 msdn says that this method returns String.Empty
310 // for absent attribute, but in fact it returns null.
311 // This description is corrected in MS.NET 1.1 msdn.
312 public override string GetAttribute (string name)
314 for (int i = 0; i < attributeCount; i++)
315 if (attributeTokens [i].Name == name)
316 return attributeTokens [i].Value;
320 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
322 for (int i = 0; i < attributeCount; i++) {
323 XmlAttributeTokenInfo ti = attributeTokens [i];
324 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
330 internal XmlParserContext GetInternalParserContext ()
332 return parserContext;
335 public override string GetAttribute (string localName, string namespaceURI)
337 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
340 return attributeTokens [idx].Value;
343 public TextReader GetRemainder ()
345 if (peekCharsIndex == peekCharsLength)
347 return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
350 bool IXmlLineInfo.HasLineInfo ()
355 public override string LookupNamespace (string prefix)
357 return LookupNamespace (prefix, false);
360 internal string LookupNamespace (string prefix, bool atomizedName)
362 return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
365 public override void MoveToAttribute (int i)
367 if (i >= attributeCount)
368 throw new ArgumentOutOfRangeException ("attribute index out of range.");
370 currentAttribute = i;
371 currentAttributeValue = -1;
372 cursorToken = attributeTokens [i];
375 public override bool MoveToAttribute (string name)
377 for (int i = 0; i < attributeCount; i++) {
378 XmlAttributeTokenInfo ti = attributeTokens [i];
379 if (ti.Name == name) {
387 public override bool MoveToAttribute (string localName, string namespaceName)
389 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
392 MoveToAttribute (idx);
396 public override bool MoveToElement ()
398 if (currentToken == null) // for attribute .ctor()
401 if (cursorToken == currentToken)
404 if (currentAttribute >= 0) {
405 currentAttribute = -1;
406 currentAttributeValue = -1;
407 cursorToken = currentToken;
414 public override bool MoveToFirstAttribute ()
416 if (attributeCount == 0)
419 return MoveToNextAttribute ();
422 public override bool MoveToNextAttribute ()
424 if (currentAttribute == 0 && attributeCount == 0)
426 if (currentAttribute + 1 < attributeCount) {
428 currentAttributeValue = -1;
429 cursorToken = attributeTokens [currentAttribute];
436 public override bool Read ()
438 if (startNodeType == XmlNodeType.Attribute) {
439 if (currentAttribute == 0)
440 return false; // already read.
442 IncrementAttributeToken ();
443 ReadAttributeValueTokens ('"');
444 cursorToken = attributeTokens [0];
445 currentAttributeValue = -1;
446 readState = ReadState.Interactive;
451 readState = ReadState.Interactive;
452 currentLinkedNodeLineNumber = line;
453 currentLinkedNodeLinePosition = column;
454 useProceedingLineInfo = true;
456 cursorToken = currentToken;
458 currentAttribute = currentAttributeValue = -1;
459 currentToken.Clear ();
461 // It was moved from end of ReadStartTag ().
466 if (shouldSkipUntilEndTag) {
467 shouldSkipUntilEndTag = false;
468 return ReadUntilEndTag ();
471 base64CacheStartsAt = -1;
473 more = ReadContent ();
475 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
476 currentState = XmlNodeType.EndElement;
478 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
479 throw new XmlException ("Document element did not appear.");
481 useProceedingLineInfo = false;
485 public override bool ReadAttributeValue ()
487 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
491 if (currentAttribute < 0)
493 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
494 if (currentAttributeValue < 0)
495 currentAttributeValue = ti.ValueTokenStartIndex - 1;
497 if (currentAttributeValue < ti.ValueTokenEndIndex) {
498 currentAttributeValue++;
499 cursorToken = attributeValueTokens [currentAttributeValue];
506 public int ReadBase64 (byte [] buffer, int offset, int length)
509 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
511 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
512 else if (buffer.Length < offset + length)
513 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
515 if (length == 0) // It does not raise an error.
518 int bufIndex = offset;
519 int bufLast = offset + length;
521 if (base64CacheStartsAt >= 0) {
522 for (int i = base64CacheStartsAt; i < 3; i++) {
523 buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
524 if (bufIndex == bufLast)
525 return bufLast - offset;
529 for (int i = 0; i < 3; i++)
531 base64CacheStartsAt = -1;
533 int max = (int) System.Math.Ceiling (4.0 / 3 * length);
534 int additional = max % 4;
536 max += 4 - additional;
537 char [] chars = new char [max];
538 int charsLength = ReadChars (chars, 0, max);
542 for (int i = 0; i < charsLength - 3; i += 4) {
543 b = (byte) (GetBase64Byte (chars [i]) << 2);
544 if (bufIndex < bufLast)
545 buffer [bufIndex] = b;
547 if (base64CacheStartsAt < 0)
548 base64CacheStartsAt = 0;
551 // charsLength mod 4 might not equals to 0.
552 if (i + 1 == charsLength)
554 b = GetBase64Byte (chars [i + 1]);
555 work = (byte) (b >> 4);
556 if (bufIndex < bufLast) {
557 buffer [bufIndex] += work;
561 base64Cache [0] += work;
563 work = (byte) ((b & 0xf) << 4);
564 if (bufIndex < bufLast) {
565 buffer [bufIndex] = work;
568 if (base64CacheStartsAt < 0)
569 base64CacheStartsAt = 1;
570 base64Cache [1] = work;
573 if (i + 2 == charsLength)
575 b = GetBase64Byte (chars [i + 2]);
576 work = (byte) (b >> 2);
577 if (bufIndex < bufLast) {
578 buffer [bufIndex] += work;
582 base64Cache [1] += work;
584 work = (byte) ((b & 3) << 6);
585 if (bufIndex < bufLast)
586 buffer [bufIndex] = work;
588 if (base64CacheStartsAt < 0)
589 base64CacheStartsAt = 2;
590 base64Cache [2] = work;
592 if (i + 3 == charsLength)
594 work = GetBase64Byte (chars [i + 3]);
595 if (bufIndex < bufLast) {
596 buffer [bufIndex] += work;
600 base64Cache [2] += work;
602 return System.Math.Min (bufLast - offset, bufIndex - offset);
605 public int ReadBinHex (byte [] buffer, int offset, int length)
608 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
610 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
611 else if (buffer.Length < offset + length)
612 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
617 char [] chars = new char [length * 2];
618 int charsLength = ReadChars (chars, 0, length * 2);
619 return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
622 public int ReadChars (char [] buffer, int offset, int length)
624 return ReadCharsInternal (buffer, offset, length);
628 public override string ReadInnerXml ()
630 return ReadInnerXmlInternal ();
633 public override string ReadOuterXml ()
635 return ReadOuterXmlInternal ();
638 public override string ReadString ()
640 return ReadStringInternal ();
644 public void ResetState ()
649 public override void ResolveEntity ()
651 // XmlTextReader does not resolve entities.
652 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
656 [MonoTODO ("Implement for performance reason")]
657 public override void Skip ()
665 // Parsed DTD Objects
666 #if DTD_HANDLE_EVENTS
667 internal event ValidationEventHandler ValidationEventHandler;
670 internal DTDObjectModel DTD {
671 get { return parserContext.Dtd; }
674 internal XmlResolver Resolver {
675 get { return resolver; }
680 internal class XmlTokenInfo
682 public XmlTokenInfo (XmlTextReader xtr, bool isPrimaryToken)
684 this.isPrimaryToken = isPrimaryToken;
692 protected XmlTextReader Reader;
695 public string LocalName;
696 public string Prefix;
697 public string NamespaceURI;
698 public bool IsEmptyElement;
699 public char QuoteChar;
700 public int LineNumber;
701 public int LinePosition;
703 public XmlNodeType NodeType;
705 public virtual string Value {
707 if (valueCache != null)
710 case XmlNodeType.Text:
711 case XmlNodeType.SignificantWhitespace:
712 case XmlNodeType.Whitespace:
713 case XmlNodeType.Comment:
714 case XmlNodeType.CDATA:
715 case XmlNodeType.ProcessingInstruction:
716 valueCache = Reader.CreateValueString ();
721 set { valueCache = value; }
724 public virtual void Clear ()
727 NodeType = XmlNodeType.None;
728 Name = LocalName = Prefix = NamespaceURI = String.Empty;
729 IsEmptyElement = false;
731 LineNumber = LinePosition = 0;
734 internal virtual void FillNames ()
736 if (Reader.Namespaces) {
737 int indexOfColon = -1;
739 case XmlNodeType.Attribute:
740 case XmlNodeType.Element:
741 case XmlNodeType.EndElement:
742 indexOfColon = Name.IndexOf (':');
746 if (indexOfColon == -1) {
747 Prefix = String.Empty;
750 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
751 // However, this might be reverted if NameTable is got improved.
752 char [] nameArr = Name.ToCharArray ();
753 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
754 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
755 // Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
756 // LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
761 case XmlNodeType.Attribute:
762 if (Prefix.Length == 0)
763 NamespaceURI = string.Empty;
765 NamespaceURI = Reader.LookupNamespace (Prefix, true);
768 case XmlNodeType.Element:
769 case XmlNodeType.EndElement:
770 NamespaceURI = Reader.LookupNamespace (Prefix, true);
777 Prefix = String.Empty;
783 internal class XmlAttributeTokenInfo : XmlTokenInfo
785 public XmlAttributeTokenInfo (XmlTextReader reader)
786 : base (reader, false)
788 NodeType = XmlNodeType.Attribute;
791 public int ValueTokenStartIndex;
792 public int ValueTokenEndIndex;
794 bool cachedNormalization;
795 StringBuilder tmpBuilder = new StringBuilder ();
797 public override string Value {
799 if (cachedNormalization != Reader.Normalization)
801 if (valueCache != null)
804 cachedNormalization = Reader.Normalization;
806 // An empty value should return String.Empty.
807 if (ValueTokenStartIndex == ValueTokenEndIndex) {
808 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
809 if (ti.NodeType == XmlNodeType.EntityReference)
810 valueCache = String.Concat ("&", ti.Name, ";");
812 valueCache = ti.Value;
813 if (cachedNormalization)
818 tmpBuilder.Length = 0;
819 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
820 XmlTokenInfo ti = Reader.attributeValueTokens [i];
821 if (ti.NodeType == XmlNodeType.Text)
822 tmpBuilder.Append (ti.Value);
824 tmpBuilder.Append ('&');
825 tmpBuilder.Append (ti.Name);
826 tmpBuilder.Append (';');
830 valueCache = tmpBuilder.ToString ();
831 if (cachedNormalization)
836 set { valueCache = value; }
839 public override void Clear ()
843 NodeType = XmlNodeType.Attribute;
844 ValueTokenStartIndex = ValueTokenEndIndex = 0;
847 internal override void FillNames ()
850 if (Prefix == "xmlns" || Name == "xmlns")
851 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
854 private void NormalizeSpaces ()
856 tmpBuilder.Length = 0;
857 for (int i = 0; i < valueCache.Length; i++)
858 switch (valueCache [i]) {
860 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
865 tmpBuilder.Append (' ');
868 tmpBuilder.Append (valueCache [i]);
871 valueCache = tmpBuilder.ToString ();
875 private XmlTokenInfo cursorToken;
876 private XmlTokenInfo currentToken;
877 private XmlAttributeTokenInfo currentAttributeToken;
878 private XmlTokenInfo currentAttributeValueToken;
879 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
880 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
881 private int currentAttribute;
882 private int currentAttributeValue;
883 private int attributeCount;
885 private XmlParserContext parserContext;
887 private ReadState readState;
890 private int elementDepth;
891 private bool depthUp;
893 private bool popScope;
895 private string [] elementNames;
896 int elementNameStackPos;
898 private bool allowMultipleRoot;
900 private bool isStandalone;
902 private bool returnEntityReference;
903 private string entityReferenceName;
905 private char [] nameBuffer;
906 private int nameLength;
907 private int nameCapacity;
908 private const int initialNameCapacity = 32;
910 private char [] valueBuffer;
911 private int valueLength;
912 private int valueCapacity;
913 private const int initialValueCapacity = 256;
915 private char [] currentTagBuffer;
916 private int currentTagLength;
917 private int currentTagCapacity;
918 private const int initialCurrentTagCapacity = 256;
920 private TextReader reader;
921 private char [] peekChars;
922 private int peekCharsIndex;
923 private int peekCharsLength;
924 private const int peekCharCapacity = 1024;
929 private int currentLinkedNodeLineNumber;
930 private int currentLinkedNodeLinePosition;
931 private bool useProceedingLineInfo;
933 private XmlNodeType startNodeType;
934 // State machine attribute.
935 // XmlDeclaration: after the first node.
936 // DocumentType: after doctypedecl
937 // Element: inside document element
938 // EndElement: after document element
939 private XmlNodeType currentState;
941 // For ReadChars()/ReadBase64()/ReadBinHex()
942 private bool shouldSkipUntilEndTag;
943 private byte [] base64Cache = new byte [3];
944 private int base64CacheStartsAt;
946 // These values are never re-initialized.
947 private bool namespaces = true;
948 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
949 private XmlResolver resolver = new XmlUrlResolver ();
950 private bool normalization = false;
954 currentToken = new XmlTokenInfo (this, true);
955 cursorToken = currentToken;
956 currentAttribute = -1;
957 currentAttributeValue = -1;
960 readState = ReadState.Initial;
961 allowMultipleRoot = false;
967 popScope = allowMultipleRoot = false;
968 elementNames = new string [10];
969 elementNameStackPos = 0;
971 isStandalone = false;
972 returnEntityReference = false;
973 entityReferenceName = String.Empty;
975 nameBuffer = new char [initialNameCapacity];
977 nameCapacity = initialNameCapacity;
979 valueBuffer = new char [initialValueCapacity];
981 valueCapacity = initialValueCapacity;
983 currentTagBuffer = new char [initialCurrentTagCapacity];
984 currentTagLength = 0;
985 currentTagCapacity = initialCurrentTagCapacity;
989 if (peekChars == null)
990 peekChars = new char [peekCharCapacity];
994 currentTagLength = 0;
996 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
997 useProceedingLineInfo = false;
999 currentState = XmlNodeType.None;
1001 shouldSkipUntilEndTag = false;
1002 base64CacheStartsAt = -1;
1005 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1007 startNodeType = fragType;
1008 parserContext = context;
1009 if (context == null) {
1010 XmlNameTable nt = new NameTable ();
1011 parserContext = new XmlParserContext (nt,
1012 new XmlNamespaceManager (nt),
1017 if (url != null && url.Length > 0) {
1020 uri = new Uri (url);
1021 } catch (Exception) {
1022 string path = Path.GetFullPath ("./a");
1023 uri = new Uri (new Uri (path), url);
1025 parserContext.BaseURI = uri.ToString ();
1031 case XmlNodeType.Attribute:
1032 fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1034 case XmlNodeType.Element:
1035 currentState = XmlNodeType.Element;
1036 allowMultipleRoot = true;
1038 case XmlNodeType.Document:
1041 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1047 // Use this method rather than setting the properties
1048 // directly so that all the necessary properties can
1049 // be changed in harmony with each other. Maybe the
1050 // fields should be in a seperate class to help enforce
1052 private void SetProperties (
1053 XmlNodeType nodeType,
1055 bool isEmptyElement,
1057 bool clearAttributes)
1059 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1060 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1061 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1064 private void SetProperties (
1066 XmlNodeType nodeType,
1068 bool isEmptyElement,
1070 bool clearAttributes)
1073 token.NodeType = nodeType;
1075 token.IsEmptyElement = isEmptyElement;
1076 token.Value = value;
1077 this.elementDepth = depth;
1079 if (clearAttributes)
1085 private void ClearAttributes ()
1087 for (int i = 0; i < attributeCount; i++)
1088 attributeTokens [i].Clear ();
1090 currentAttribute = -1;
1091 currentAttributeValue = -1;
1094 private int PeekChar ()
1096 if (peekCharsLength == peekCharsIndex) {
1097 if (!ReadTextReader ())
1102 return peekChars [peekCharsIndex];
1105 private int ReadChar ()
1109 if (peekCharsLength == peekCharsIndex) {
1110 if (!ReadTextReader ())
1114 ch = peekChars [peekCharsIndex++];
1122 if (currentState != XmlNodeType.Element)
1123 AppendCurrentTagChar (ch);
1127 private bool ReadTextReader ()
1130 peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity);
1131 if (peekCharsLength == 0)
1136 private string ExpandSurrogateChar (int ch)
1138 if (ch < Char.MaxValue)
1139 return ((char) ch).ToString ();
1141 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1142 return new string (tmp);
1146 // This should really keep track of some state so
1147 // that it's not possible to have more than one document
1148 // element or text outside of the document element.
1149 private bool ReadContent ()
1151 currentTagLength = 0;
1153 parserContext.NamespaceManager.PopScope ();
1157 if (returnEntityReference)
1158 SetEntityReferenceProperties ();
1160 switch (PeekChar ()) {
1165 case '\r': goto case ' ';
1166 case '\n': goto case ' ';
1167 case '\t': goto case ' ';
1169 if (whitespaceHandling == WhitespaceHandling.All ||
1170 whitespaceHandling == WhitespaceHandling.Significant)
1174 return ReadContent ();
1178 readState = ReadState.EndOfFile;
1179 ClearValueBuffer ();
1181 XmlNodeType.None, // nodeType
1182 String.Empty, // name
1183 false, // isEmptyElement
1185 true // clearAttributes
1188 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1196 return this.ReadState != ReadState.EndOfFile;
1199 private void SetEntityReferenceProperties ()
1201 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1202 if (this.isStandalone)
1203 if (DTD == null || decl == null || !decl.IsInternalSubset)
1204 throw new XmlException (this as IXmlLineInfo,
1205 "Standalone document must not contain any references to an non-internally declared entity.");
1206 if (decl != null && decl.NotationName != null)
1207 throw new XmlException (this as IXmlLineInfo,
1208 "Reference to any unparsed entities is not allowed here.");
1210 ClearValueBuffer ();
1212 XmlNodeType.EntityReference, // nodeType
1213 entityReferenceName, // name
1214 false, // isEmptyElement
1216 true // clearAttributes
1219 returnEntityReference = false;
1220 entityReferenceName = String.Empty;
1223 // The leading '<' has already been consumed.
1224 private void ReadTag ()
1226 switch (PeekChar ())
1234 ReadProcessingInstruction ();
1246 // The leading '<' has already been consumed.
1247 private void ReadStartTag ()
1249 if (currentState == XmlNodeType.EndElement)
1250 throw new XmlException (this as IXmlLineInfo,
1251 "Element cannot appear in this state.");
1252 currentState = XmlNodeType.Element;
1254 parserContext.NamespaceManager.PushScope ();
1256 string name = ReadName ();
1257 if (currentState == XmlNodeType.EndElement)
1258 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1260 bool isEmptyElement = false;
1265 if (XmlChar.IsFirstNameChar (PeekChar ()))
1266 ReadAttributes (false);
1267 cursorToken = this.currentToken;
1270 for (int i = 0; i < attributeCount; i++)
1271 attributeTokens [i].FillNames ();
1274 for (int i = 0; i < attributeCount; i++)
1275 for (int j = i + 1; j < attributeCount; j++)
1276 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1277 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1278 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1279 throw new XmlException (this as IXmlLineInfo,
1280 "Attribute name and qualified name must be identical.");
1282 string baseUri = GetAttribute ("xml:base");
1283 if (baseUri != null) {
1284 if (this.resolver != null)
1285 parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1287 parserContext.BaseURI = baseUri;
1289 string xmlLang = GetAttribute ("xml:lang");
1290 if (xmlLang != null)
1291 parserContext.XmlLang = xmlLang;
1292 string xmlSpaceAttr = GetAttribute ("xml:space");
1293 if (xmlSpaceAttr != null) {
1294 if (xmlSpaceAttr == "preserve")
1295 parserContext.XmlSpace = XmlSpace.Preserve;
1296 else if (xmlSpaceAttr == "default")
1297 parserContext.XmlSpace = XmlSpace.Default;
1299 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1301 if (PeekChar () == '/') {
1303 isEmptyElement = true;
1308 PushElementName (name);
1309 parserContext.PushScope ();
1314 XmlNodeType.Element, // nodeType
1316 isEmptyElement, // isEmptyElement
1318 false // clearAttributes
1322 private void PushElementName (string name)
1324 if (elementNames.Length == elementNameStackPos) {
1325 string [] newArray = new string [elementNames.Length * 2];
1326 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1327 elementNames = newArray;
1329 elementNames [elementNameStackPos++] = name;
1332 // The reader is positioned on the first character
1333 // of the element's name.
1334 private void ReadEndTag ()
1336 if (currentState != XmlNodeType.Element)
1337 throw new XmlException (this as IXmlLineInfo,
1338 "End tag cannot appear in this state.");
1340 string name = ReadName ();
1341 if (elementNameStackPos == 0)
1342 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1343 string expected = elementNames [--elementNameStackPos];
1344 if (expected != name)
1345 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1346 parserContext.PopScope ();
1348 ExpectAfterWhitespace ('>');
1353 XmlNodeType.EndElement, // nodeType
1355 false, // isEmptyElement
1357 true // clearAttributes
1363 private void AppendNameChar (int ch)
1365 if (nameLength == nameCapacity)
1366 ExpandNameCapacity ();
1367 if (ch < Char.MaxValue)
1368 nameBuffer [nameLength++] = (char) ch;
1370 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1371 if (nameLength == nameCapacity)
1372 ExpandNameCapacity ();
1373 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1377 private void ExpandNameCapacity ()
1379 nameCapacity = nameCapacity * 2;
1380 char [] oldNameBuffer = nameBuffer;
1381 nameBuffer = new char [nameCapacity];
1382 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1385 private string CreateNameString ()
1387 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1390 private void AppendValueChar (int ch)
1392 if (valueLength == valueCapacity)
1393 ExpandValueCapacity ();
1394 if (ch < Char.MaxValue)
1395 valueBuffer [valueLength++] = (char) ch;
1397 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1398 if (valueLength == valueCapacity)
1399 ExpandValueCapacity ();
1400 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1404 private void ExpandValueCapacity ()
1406 valueCapacity = valueCapacity * 2;
1407 char [] oldValueBuffer = valueBuffer;
1408 valueBuffer = new char [valueCapacity];
1409 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1412 private string CreateValueString ()
1414 return new string (valueBuffer, 0, valueLength);
1417 private void ClearValueBuffer ()
1422 private void AppendCurrentTagChar (int ch)
1424 if (currentTagLength == currentTagCapacity)
1425 ExpandCurrentTagCapacity ();
1426 if (ch < Char.MaxValue)
1427 currentTagBuffer [currentTagLength++] = (char) ch;
1429 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1430 if (currentTagLength == currentTagCapacity)
1431 ExpandCurrentTagCapacity ();
1432 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1436 private void ExpandCurrentTagCapacity ()
1438 currentTagCapacity = currentTagCapacity * 2;
1439 char [] oldCurrentTagBuffer = currentTagBuffer;
1440 currentTagBuffer = new char [currentTagCapacity];
1441 Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1444 private string CreateCurrentTagString ()
1446 return new string (currentTagBuffer, 0, currentTagLength);
1449 private void ClearCurrentTagBuffer ()
1451 currentTagLength = 0;
1454 // The reader is positioned on the first character
1456 private void ReadText (bool notWhitespace)
1458 if (currentState != XmlNodeType.Element)
1459 throw new XmlException (this as IXmlLineInfo,
1460 "Text node cannot appear in this state.");
1463 ClearValueBuffer ();
1465 int ch = PeekChar ();
1466 bool previousWasCloseBracket = false;
1468 while (ch != '<' && ch != -1) {
1471 ch = ReadReference (false);
1472 if (returnEntityReference) // Returns -1 if char validation should not be done
1478 if (normalization && XmlChar.IsInvalid (ch))
1479 throw new XmlException (this, "Not allowed character was found.");
1480 AppendValueChar (ch);
1484 if (previousWasCloseBracket)
1485 if (PeekChar () == '>')
1486 throw new XmlException (this as IXmlLineInfo,
1487 "Inside text content, character sequence ']]>' is not allowed.");
1488 previousWasCloseBracket = true;
1490 else if (previousWasCloseBracket)
1491 previousWasCloseBracket = false;
1493 notWhitespace = true;
1496 if (returnEntityReference && valueLength == 0) {
1497 SetEntityReferenceProperties ();
1499 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1500 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1502 nodeType, // nodeType
1503 String.Empty, // name
1504 false, // isEmptyElement
1505 null, // value: create only when required
1506 true // clearAttributes
1511 // The leading '&' has already been consumed.
1512 // Returns true if the entity reference isn't a simple
1513 // character reference or one of the predefined entities.
1514 // This allows the ReadText method to break so that the
1515 // next call to Read will return the EntityReference node.
1516 private int ReadReference (bool ignoreEntityReferences)
1518 if (PeekChar () == '#') {
1520 return ReadCharacterReference ();
1522 return ReadEntityReference (ignoreEntityReferences);
1525 private int ReadCharacterReference ()
1529 if (PeekChar () == 'x') {
1532 while (PeekChar () != ';' && PeekChar () != -1) {
1533 int ch = ReadChar ();
1535 if (ch >= '0' && ch <= '9')
1536 value = (value << 4) + ch - '0';
1537 else if (ch >= 'A' && ch <= 'F')
1538 value = (value << 4) + ch - 'A' + 10;
1539 else if (ch >= 'a' && ch <= 'f')
1540 value = (value << 4) + ch - 'a' + 10;
1542 throw new XmlException (this as IXmlLineInfo,
1544 "invalid hexadecimal digit: {0} (#x{1:X})",
1549 while (PeekChar () != ';' && PeekChar () != -1) {
1550 int ch = ReadChar ();
1552 if (ch >= '0' && ch <= '9')
1553 value = value * 10 + ch - '0';
1555 throw new XmlException (this as IXmlLineInfo,
1557 "invalid decimal digit: {0} (#x{1:X})",
1565 // There is no way to save surrogate pairs...
1566 if (normalization && XmlChar.IsInvalid (value))
1567 throw new XmlException (this as IXmlLineInfo,
1568 "Referenced character was not allowed in XML.");
1572 // Returns -1 if it should not be validated.
1573 // Real EOF must not be detected here.
1574 private int ReadEntityReference (bool ignoreEntityReferences)
1576 string name = ReadName ();
1579 int predefined = XmlChar.GetPredefinedEntity (name);
1580 if (predefined >= 0)
1583 if (ignoreEntityReferences) {
1584 AppendValueChar ('&');
1585 for (int i = 0; i < name.Length; i++)
1586 AppendValueChar (name [i]);
1587 AppendValueChar (';');
1589 returnEntityReference = true;
1590 entityReferenceName = name;
1596 // The reader is positioned on the first character of
1597 // the attribute name.
1598 private void ReadAttributes (bool isXmlDecl)
1601 bool requireWhitespace = false;
1602 currentAttribute = -1;
1603 currentAttributeValue = -1;
1606 if (!SkipWhitespace () && requireWhitespace)
1607 throw new XmlException ("Unexpected token. Name is required here.");
1609 IncrementAttributeToken ();
1610 currentAttributeToken.LineNumber = line;
1611 currentAttributeToken.LinePosition = column;
1613 currentAttributeToken.LocalName =
1614 currentAttributeToken.Name = ReadName ();
1615 ExpectAfterWhitespace ('=');
1617 ReadAttributeValueTokens (-1);
1620 if (currentAttributeToken.Name == "xmlns")
1621 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1622 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1623 string nsPrefix = currentAttributeToken.Name.Substring (6);
1624 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1627 if (!SkipWhitespace ())
1628 requireWhitespace = true;
1629 peekChar = PeekChar ();
1631 if (peekChar == '?')
1634 else if (peekChar == '/' || peekChar == '>')
1636 } while (peekChar != -1);
1638 currentAttribute = -1;
1639 currentAttributeValue = -1;
1642 private void AddAttribute (string name, string value)
1644 IncrementAttributeToken ();
1645 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1646 ati.Name = "SYSTEM";
1648 IncrementAttributeValueToken ();
1649 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1651 SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1655 private void IncrementAttributeToken ()
1658 if (attributeTokens.Length == currentAttribute) {
1659 XmlAttributeTokenInfo [] newArray =
1660 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1661 attributeTokens.CopyTo (newArray, 0);
1662 attributeTokens = newArray;
1664 if (attributeTokens [currentAttribute] == null)
1665 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1666 currentAttributeToken = attributeTokens [currentAttribute];
1667 currentAttributeToken.Clear ();
1670 private void IncrementAttributeValueToken ()
1672 ClearValueBuffer ();
1673 currentAttributeValue++;
1674 if (attributeValueTokens.Length == currentAttributeValue) {
1675 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1676 attributeValueTokens.CopyTo (newArray, 0);
1677 attributeValueTokens = newArray;
1679 if (attributeValueTokens [currentAttributeValue] == null)
1680 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1681 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1682 currentAttributeValueToken.Clear ();
1685 // FIXME: normalize here
1686 private void ReadAttributeValueTokens (int dummyQuoteChar)
1688 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1690 if (quoteChar != '\'' && quoteChar != '\"')
1691 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1692 currentAttributeToken.QuoteChar = (char) quoteChar;
1694 IncrementAttributeValueToken ();
1695 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1696 currentAttributeValueToken.LineNumber = line;
1697 currentAttributeValueToken.LinePosition = column;
1699 bool incrementToken = false;
1700 bool isNewToken = true;
1705 if (ch == quoteChar)
1708 if (incrementToken) {
1709 IncrementAttributeValueToken ();
1710 currentAttributeValueToken.LineNumber = line;
1711 currentAttributeValueToken.LinePosition = column;
1712 incrementToken = false;
1719 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1721 if (dummyQuoteChar < 0)
1722 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1723 else // Attribute value constructor.
1727 int startPosition = currentTagLength - 1;
1728 if (PeekChar () == '#') {
1730 ch = ReadCharacterReference ();
1731 if (normalization && XmlChar.IsInvalid (ch))
1732 throw new XmlException (this as IXmlLineInfo,
1733 "Not allowed character was found.");
1734 AppendValueChar (ch);
1737 // Check XML 1.0 section 3.1 WFC.
1738 string entName = ReadName ();
1740 int predefined = XmlChar.GetPredefinedEntity (entName);
1741 if (predefined < 0) {
1742 CheckAttributeEntityReferenceWFC (entName);
1743 currentAttributeValueToken.Value = CreateValueString ();
1744 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1746 IncrementAttributeValueToken ();
1747 currentAttributeValueToken.Name = entName;
1748 currentAttributeValueToken.Value = String.Empty;
1749 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1750 incrementToken = true;
1753 AppendValueChar (predefined);
1756 if (normalization && XmlChar.IsInvalid (ch))
1757 throw new XmlException (this, "Invalid character was found.");
1758 AppendValueChar (ch);
1764 if (!incrementToken) {
1765 currentAttributeValueToken.Value = CreateValueString ();
1766 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1768 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1772 private void CheckAttributeEntityReferenceWFC (string entName)
1774 DTDEntityDeclaration entDecl =
1775 DTD == null ? null : DTD.EntityDecls [entName];
1776 if (DTD != null && resolver != null && entDecl == null)
1777 throw new XmlException (this, "Referenced entity does not exist.");
1779 if (entDecl == null)
1782 if (entDecl.HasExternalReference)
1783 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
1784 if (isStandalone && !entDecl.IsInternalSubset)
1785 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
1786 if (entDecl.EntityValue.IndexOf ('<') >= 0)
1787 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1790 // The reader is positioned on the first character
1793 // It may be xml declaration or processing instruction.
1794 private void ReadProcessingInstruction ()
1796 string target = ReadName ();
1797 if (target == "xml") {
1798 ReadXmlDeclaration ();
1800 } else if (target.ToLower () == "xml")
1801 throw new XmlException (this as IXmlLineInfo,
1802 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1804 if (currentState == XmlNodeType.None)
1805 currentState = XmlNodeType.XmlDeclaration;
1807 if (!SkipWhitespace ())
1808 if (PeekChar () != '?')
1809 throw new XmlException (this as IXmlLineInfo,
1810 "Invalid processing instruction name was found.");
1812 ClearValueBuffer ();
1814 while (PeekChar () != -1) {
1815 int ch = ReadChar ();
1817 if (ch == '?' && PeekChar () == '>') {
1822 if (normalization && XmlChar.IsInvalid (ch))
1823 throw new XmlException (this, "Invalid character was found.");
1824 AppendValueChar (ch);
1828 XmlNodeType.ProcessingInstruction, // nodeType
1830 false, // isEmptyElement
1831 null, // value: create only when required
1832 true // clearAttributes
1836 // The reader is positioned after "<?xml "
1837 private void ReadXmlDeclaration ()
1839 if (currentState != XmlNodeType.None) {
1840 throw new XmlException (this as IXmlLineInfo,
1841 "XML declaration cannot appear in this state.");
1843 currentState = XmlNodeType.XmlDeclaration;
1847 ReadAttributes (true); // They must have "version."
1848 string version = GetAttribute ("version");
1850 string message = null;
1852 if (attributeTokens [0].Name != "version" || version != "1.0")
1853 message = "Version 1.0 declaration is required in XML Declaration.";
1854 else if (attributeCount > 1 &&
1855 (attributeTokens [1].Name != "encoding" &&
1856 attributeTokens [1].Name != "standalone"))
1857 message = "Invalid Xml Declaration markup was found.";
1858 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1859 message = "Invalid Xml Declaration markup was found.";
1860 string sa = GetAttribute ("standalone");
1861 if (sa != null && sa != "yes" && sa != "no")
1862 message = "Only 'yes' or 'no' is allowed for standalone.";
1864 this.isStandalone = (sa == "yes");
1866 if (message != null)
1867 throw new XmlException (this as IXmlLineInfo, message);
1870 XmlNodeType.XmlDeclaration, // nodeType
1872 false, // isEmptyElement
1873 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1874 false // clearAttributes
1880 internal void SkipTextDeclaration ()
1882 this.currentState = XmlNodeType.Element;
1884 if (PeekChar () != '<')
1889 if (PeekChar () != '?') {
1895 while (peekCharsIndex < 6) {
1896 if (PeekChar () < 0)
1901 if (new string (peekChars, 2, 4) != "xml ") {
1902 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1903 throw new XmlException (this as IXmlLineInfo,
1904 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1913 if (PeekChar () == 'v') {
1915 ExpectAfterWhitespace ('=');
1917 int quoteChar = ReadChar ();
1918 char [] expect1_0 = new char [3];
1919 int versionLength = 0;
1920 switch (quoteChar) {
1923 while (PeekChar () != quoteChar) {
1924 if (PeekChar () == -1)
1925 throw new XmlException (this as IXmlLineInfo,
1926 "Invalid version declaration inside text declaration.");
1927 else if (versionLength == 3)
1928 throw new XmlException (this as IXmlLineInfo,
1929 "Invalid version number inside text declaration.");
1931 expect1_0 [versionLength] = (char) ReadChar ();
1933 if (versionLength == 3 && new String (expect1_0) != "1.0")
1934 throw new XmlException (this as IXmlLineInfo,
1935 "Invalid version number inside text declaration.");
1942 throw new XmlException (this as IXmlLineInfo,
1943 "Invalid version declaration inside text declaration.");
1947 if (PeekChar () == 'e') {
1948 Expect ("encoding");
1949 ExpectAfterWhitespace ('=');
1951 int quoteChar = ReadChar ();
1952 switch (quoteChar) {
1955 while (PeekChar () != quoteChar)
1956 if (ReadChar () == -1)
1957 throw new XmlException (this as IXmlLineInfo,
1958 "Invalid encoding declaration inside text declaration.");
1963 throw new XmlException (this as IXmlLineInfo,
1964 "Invalid encoding declaration inside text declaration.");
1966 // Encoding value should be checked inside XmlInputStream.
1969 throw new XmlException (this as IXmlLineInfo,
1970 "Encoding declaration is mandatory in text declaration.");
1975 // The reader is positioned on the first character after
1976 // the leading '<!'.
1977 private void ReadDeclaration ()
1979 int ch = PeekChar ();
1997 throw new XmlException (this as IXmlLineInfo,
1998 "Unexpected declaration markup was found.");
2002 // The reader is positioned on the first character after
2003 // the leading '<!--'.
2004 private void ReadComment ()
2006 if (currentState == XmlNodeType.None)
2007 currentState = XmlNodeType.XmlDeclaration;
2009 ClearValueBuffer ();
2011 while (PeekChar () != -1) {
2012 int ch = ReadChar ();
2014 if (ch == '-' && PeekChar () == '-') {
2017 if (PeekChar () != '>')
2018 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2024 if (XmlChar.IsInvalid (ch))
2025 throw new XmlException (this as IXmlLineInfo,
2026 "Not allowed character was found.");
2028 AppendValueChar (ch);
2032 XmlNodeType.Comment, // nodeType
2033 String.Empty, // name
2034 false, // isEmptyElement
2035 null, // value: create only when required
2036 true // clearAttributes
2040 // The reader is positioned on the first character after
2041 // the leading '<![CDATA['.
2042 private void ReadCDATA ()
2044 if (currentState != XmlNodeType.Element)
2045 throw new XmlException (this as IXmlLineInfo,
2046 "CDATA section cannot appear in this state.");
2048 ClearValueBuffer ();
2052 while (PeekChar () != -1) {
2057 if (ch == ']' && PeekChar () == ']') {
2058 ch = ReadChar (); // ']'
2060 if (PeekChar () == '>') {
2067 if (normalization && XmlChar.IsInvalid (ch))
2068 throw new XmlException (this, "Invalid character was found.");
2070 AppendValueChar (ch);
2074 XmlNodeType.CDATA, // nodeType
2075 String.Empty, // name
2076 false, // isEmptyElement
2077 null, // value: create only when required
2078 true // clearAttributes
2082 // The reader is positioned on the first character after
2083 // the leading '<!DOCTYPE'.
2084 private void ReadDoctypeDecl ()
2086 switch (currentState) {
2087 case XmlNodeType.DocumentType:
2088 case XmlNodeType.Element:
2089 case XmlNodeType.EndElement:
2090 throw new XmlException (this as IXmlLineInfo,
2091 "Document type cannot appear in this state.");
2093 currentState = XmlNodeType.DocumentType;
2095 string doctypeName = null;
2096 string publicId = null;
2097 string systemId = null;
2098 int intSubsetStartLine = 0;
2099 int intSubsetStartColumn = 0;
2102 doctypeName = ReadName ();
2107 systemId = ReadSystemLiteral (true);
2110 publicId = ReadPubidLiteral ();
2111 if (!SkipWhitespace ())
2112 throw new XmlException (this as IXmlLineInfo,
2113 "Whitespace is required between PUBLIC id and SYSTEM id.");
2114 systemId = ReadSystemLiteral (false);
2120 if(PeekChar () == '[')
2122 // read markupdecl etc. or end of decl
2124 intSubsetStartLine = this.LineNumber;
2125 intSubsetStartColumn = this.LinePosition;
2126 int startPos = currentTagLength;
2127 ReadInternalSubset ();
2128 int endPos = currentTagLength - 1;
2129 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2131 // end of DOCTYPE decl.
2132 ExpectAfterWhitespace ('>');
2134 GenerateDTDObjectModel (doctypeName, publicId,
2135 systemId, parserContext.InternalSubset,
2136 intSubsetStartLine, intSubsetStartColumn);
2138 // set properties for <!DOCTYPE> node
2140 XmlNodeType.DocumentType, // nodeType
2141 doctypeName, // name
2142 false, // isEmptyElement
2143 parserContext.InternalSubset, // value
2144 true // clearAttributes
2147 if (publicId != null)
2148 AddAttribute ("PUBLIC", publicId);
2149 if (systemId != null)
2150 AddAttribute ("SYSTEM", systemId);
2151 currentAttribute = currentAttributeValue = -1;
2154 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2155 string systemId, string internalSubset)
2157 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2160 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2161 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2164 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2165 DTD.BaseURI = BaseURI;
2167 DTD.PublicId = publicId;
2168 DTD.SystemId = systemId;
2169 DTD.InternalSubset = internalSubset;
2170 DTD.XmlResolver = resolver;
2171 DTD.IsStandalone = isStandalone;
2172 DTD.LineNumber = line;
2173 DTD.LinePosition = column;
2175 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2176 dr.Normalization = this.normalization;
2177 #if DTD_HANDLE_EVENTS
2178 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2180 return dr.GenerateDTDObjectModel ();
2183 private void OnValidationEvent (object o, ValidationEventArgs e)
2185 #if DTD_HANDLE_EVENTS
2186 if (ValidationEventHandler != null)
2187 // Override object as this.
2188 ValidationEventHandler (this, e);
2192 private enum DtdInputState
2205 private class DtdInputStateStack
2207 Stack intern = new Stack ();
2208 public DtdInputStateStack ()
2210 Push (DtdInputState.Free);
2213 public DtdInputState Peek ()
2215 return (DtdInputState) intern.Peek ();
2218 public DtdInputState Pop ()
2220 return (DtdInputState) intern.Pop ();
2223 public void Push (DtdInputState val)
2230 DtdInputStateStack stateStack = new DtdInputStateStack ();
2231 DtdInputState State {
2232 get { return stateStack.Peek (); }
2235 // Simply read but not generate any result.
2236 private void ReadInternalSubset ()
2238 bool continueParse = true;
2240 while (continueParse) {
2241 switch (ReadChar ()) {
2244 case DtdInputState.Free:
2245 continueParse = false;
2247 case DtdInputState.InsideDoubleQuoted:
2249 case DtdInputState.InsideSingleQuoted:
2252 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2256 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2258 if (State == DtdInputState.InsideDoubleQuoted ||
2259 State == DtdInputState.InsideSingleQuoted)
2260 continue; // well-formed
2261 switch (ReadChar ()) {
2263 stateStack.Push (DtdInputState.PI);
2266 switch (ReadChar ()) {
2268 switch (ReadChar ()) {
2271 stateStack.Push (DtdInputState.ElementDecl);
2275 stateStack.Push (DtdInputState.EntityDecl);
2278 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2283 stateStack.Push (DtdInputState.AttlistDecl);
2287 stateStack.Push (DtdInputState.NotationDecl);
2291 stateStack.Push (DtdInputState.Comment);
2296 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2300 if (State == DtdInputState.InsideSingleQuoted)
2302 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2303 stateStack.Push (DtdInputState.InsideSingleQuoted);
2306 if (State == DtdInputState.InsideDoubleQuoted)
2308 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2309 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2313 case DtdInputState.ElementDecl:
2314 goto case DtdInputState.NotationDecl;
2315 case DtdInputState.AttlistDecl:
2316 goto case DtdInputState.NotationDecl;
2317 case DtdInputState.EntityDecl:
2318 goto case DtdInputState.NotationDecl;
2319 case DtdInputState.NotationDecl:
2322 case DtdInputState.InsideDoubleQuoted:
2324 case DtdInputState.InsideSingleQuoted:
2325 continue; // well-formed
2326 case DtdInputState.Comment:
2329 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2333 if (State == DtdInputState.PI) {
2334 if (ReadChar () == '>')
2339 if (State == DtdInputState.Comment) {
2340 if (PeekChar () == '-') {
2348 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2349 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2355 // The reader is positioned on the first 'S' of "SYSTEM".
2356 private string ReadSystemLiteral (bool expectSYSTEM)
2360 if (!SkipWhitespace ())
2361 throw new XmlException (this as IXmlLineInfo,
2362 "Whitespace is required after 'SYSTEM'.");
2366 int quoteChar = ReadChar (); // apos or quot
2367 int startPos = currentTagLength;
2369 ClearValueBuffer ();
2370 while (c != quoteChar) {
2373 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2375 AppendValueChar (c);
2377 return CreateValueString ();
2380 private string ReadPubidLiteral()
2383 if (!SkipWhitespace ())
2384 throw new XmlException (this as IXmlLineInfo,
2385 "Whitespace is required after 'PUBLIC'.");
2386 int quoteChar = ReadChar ();
2387 int startPos = currentTagLength;
2389 ClearValueBuffer ();
2390 while(c != quoteChar)
2393 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2394 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2395 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2397 AppendValueChar (c);
2399 return CreateValueString ();
2402 // The reader is positioned on the first character
2404 private string ReadName ()
2406 int ch = PeekChar ();
2407 if (!XmlChar.IsFirstNameChar (ch))
2408 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2412 AppendNameChar (ReadChar ());
2414 while (XmlChar.IsNameChar (PeekChar ())) {
2415 AppendNameChar (ReadChar ());
2418 return CreateNameString ();
2421 // Read the next character and compare it against the
2422 // specified character.
2423 private void Expect (int expected)
2425 int ch = ReadChar ();
2427 if (ch != expected) {
2428 throw new XmlException (this as IXmlLineInfo,
2430 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2438 private void Expect (string expected)
2440 int len = expected.Length;
2441 for(int i=0; i< len; i++)
2442 Expect (expected[i]);
2445 private void ExpectAfterWhitespace (char c)
2448 int i = ReadChar ();
2449 if (i < 0x21 && XmlChar.IsWhitespace (i))
2452 throw new XmlException (this, String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2457 // Does not consume the first non-whitespace character.
2458 private bool SkipWhitespace ()
2460 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2463 while (XmlChar.IsWhitespace (PeekChar ()))
2468 private void ReadWhitespace ()
2470 if (currentState == XmlNodeType.None)
2471 currentState = XmlNodeType.XmlDeclaration;
2473 ClearValueBuffer ();
2474 int ch = PeekChar ();
2476 AppendValueChar (ReadChar ());
2477 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2479 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2482 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2483 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2484 SetProperties (nodeType,
2487 null, // value: create only when required
2494 private byte GetBase64Byte (char ch)
2504 if (ch >= 'A' && ch <= 'Z')
2505 return (byte) (ch - 'A');
2506 else if (ch >= 'a' && ch <= 'z')
2507 return (byte) (ch - 'a' + 26);
2508 else if (ch >= '0' && ch <= '9')
2509 return (byte) (ch - '0' + 52);
2511 throw new XmlException ("Invalid Base64 character was found.");
2515 // Returns -1 if it should throw an error.
2516 private int ReadCharsInternal (char [] buffer, int offset, int length)
2518 shouldSkipUntilEndTag = true;
2521 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2522 else if (length < 0)
2523 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2524 else if (buffer.Length < offset + length)
2525 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2527 if (NodeType != XmlNodeType.Element)
2530 int bufIndex = offset;
2531 for (int i = 0; i < length; i++) {
2532 int c = PeekChar ();
2535 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2538 if (PeekChar () != '/') {
2539 buffer [bufIndex++] = '<';
2542 // Seems to skip immediate EndElement
2544 string name = ReadName ();
2545 if (name != elementNames [elementNameStackPos - 1]) {
2546 if (i + 1 < length) {
2547 buffer [bufIndex++] = '<';
2548 buffer [bufIndex++] = '/';
2550 for (int n = 0; n < name.Length && i + n + 1 < length; n++)
2551 buffer [bufIndex++] = name [n];
2556 elementNames [--elementNameStackPos] = null;
2557 shouldSkipUntilEndTag = false;
2562 if (c < Char.MaxValue)
2563 buffer [bufIndex++] = (char) c;
2565 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2566 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2574 private bool ReadUntilEndTag ()
2581 throw new XmlException (this as IXmlLineInfo,
2582 "Unexpected end of xml.");
2584 if (PeekChar () != '/')
2587 string name = ReadName ();
2588 if (name != elementNames [elementNameStackPos - 1])
2592 elementNames [--elementNameStackPos] = null;