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;
30 public class XmlTextReader : XmlReader, IXmlLineInfo
34 protected XmlTextReader ()
38 public XmlTextReader (Stream input)
39 : this (new XmlStreamReader (input))
43 public XmlTextReader (string url)
44 : this(url, new NameTable ())
48 public XmlTextReader (TextReader input)
49 : this (input, new NameTable ())
53 protected XmlTextReader (XmlNameTable nt)
54 : this (String.Empty, null, XmlNodeType.None, null)
58 public XmlTextReader (Stream input, XmlNameTable nt)
59 : this(new XmlStreamReader (input), nt)
63 public XmlTextReader (string url, Stream input)
64 : this (url, new XmlStreamReader (input))
68 public XmlTextReader (string url, TextReader input)
69 : this (url, input, new NameTable ())
73 public XmlTextReader (string url, XmlNameTable nt)
75 Uri uri = resolver.ResolveUri (null, url);
76 Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
77 XmlParserContext ctx = new XmlParserContext (nt,
78 new XmlNamespaceManager (nt),
81 this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document);
84 public XmlTextReader (TextReader input, XmlNameTable nt)
85 : this (String.Empty, input, nt)
89 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
90 : this (context != null ? context.BaseURI : String.Empty,
91 new XmlStreamReader (xmlFragment),
97 public XmlTextReader (string url, Stream input, XmlNameTable nt)
98 : this (url, new XmlStreamReader (input), nt)
102 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
103 : this (url, input, XmlNodeType.Document, null)
107 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
108 : this (context != null ? context.BaseURI : String.Empty,
109 new StringReader (xmlFragment),
115 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
117 InitializeContext (url, context, fragment, fragType);
124 public override int AttributeCount
126 get { return attributeCount; }
129 public override string BaseURI
131 get { return parserContext.BaseURI; }
134 public override int Depth
137 int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element ? 0 : -1;
138 if (currentAttributeValue >= 0)
139 return nodeTypeMod + elementDepth + 2; // inside attribute value.
140 else if (currentAttribute >= 0)
141 return nodeTypeMod + elementDepth + 1;
146 public Encoding Encoding
148 get { return parserContext.Encoding; }
152 public EntityHandling EntityHandling {
153 get { throw new NotImplementedException (); }
157 public override bool EOF
162 readState == ReadState.EndOfFile ||
163 readState == ReadState.Closed;
169 public override Evidence [] Evidences {
170 get { return base.Evidences; }
174 public override bool HasValue {
175 get { return cursorToken.Value != null; }
178 public override bool IsDefault {
179 // XmlTextReader does not expand default attributes.
180 get { return false; }
183 public override bool IsEmptyElement {
184 get { return cursorToken.IsEmptyElement; }
187 public override string this [int i] {
188 get { return GetAttribute (i); }
191 public override string this [string name] {
192 get { return GetAttribute (name); }
195 public override string this [string localName, string namespaceName] {
196 get { return GetAttribute (localName, namespaceName); }
199 public int LineNumber {
201 if (useProceedingLineInfo)
204 return cursorToken.LineNumber;
208 public int LinePosition {
210 if (useProceedingLineInfo)
213 return cursorToken.LinePosition;
217 public override string LocalName {
218 get { return cursorToken.LocalName; }
221 public override string Name {
222 get { return cursorToken.Name; }
225 public bool Namespaces {
226 get { return namespaces; }
228 if (readState != ReadState.Initial)
229 throw new InvalidOperationException ("Namespaces have to be set before reading.");
234 public override string NamespaceURI {
235 get { return cursorToken.NamespaceURI; }
238 public override XmlNameTable NameTable {
239 get { return parserContext.NameTable; }
242 public override XmlNodeType NodeType {
243 get { return cursorToken.NodeType; }
246 public bool Normalization {
247 get { return normalization; }
248 set { normalization = value; }
251 public override string Prefix {
252 get { return cursorToken.Prefix; }
257 [MonoTODO ("Not documented in .NET SDK")]
258 public bool ProhibitDtd {
259 get { return prohibitDtd; }
260 set { prohibitDtd = value; }
264 public override char QuoteChar {
265 get { return cursorToken.QuoteChar; }
268 public override ReadState ReadState {
269 get { return readState; }
272 public override string Value {
273 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
276 public WhitespaceHandling WhitespaceHandling {
277 get { return whitespaceHandling; }
278 set { whitespaceHandling = value; }
281 public override string XmlLang {
282 get { return parserContext.XmlLang; }
285 public XmlResolver XmlResolver {
286 set { resolver = value; }
289 public override XmlSpace XmlSpace {
290 get { return parserContext.XmlSpace; }
297 public override void Close ()
299 readState = ReadState.Closed;
301 cursorToken.Clear ();
302 currentToken.Clear ();
308 public override string GetAttribute (int i)
310 if (i >= attributeCount)
311 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
313 return attributeTokens [i].Value;
317 // MS.NET 1.0 msdn says that this method returns String.Empty
318 // for absent attribute, but in fact it returns null.
319 // This description is corrected in MS.NET 1.1 msdn.
320 public override string GetAttribute (string name)
322 for (int i = 0; i < attributeCount; i++)
323 if (attributeTokens [i].Name == name)
324 return attributeTokens [i].Value;
328 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
330 for (int i = 0; i < attributeCount; i++) {
331 XmlAttributeTokenInfo ti = attributeTokens [i];
332 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
338 internal XmlParserContext GetInternalParserContext ()
340 return parserContext;
343 public override string GetAttribute (string localName, string namespaceURI)
345 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
348 return attributeTokens [idx].Value;
351 public TextReader GetRemainder ()
353 if (peekCharsIndex == peekCharsLength)
355 return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
358 bool IXmlLineInfo.HasLineInfo ()
363 public override string LookupNamespace (string prefix)
365 return LookupNamespace (prefix, false);
368 internal string LookupNamespace (string prefix, bool atomizedName)
370 return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
373 public override void MoveToAttribute (int i)
375 if (i >= attributeCount)
376 throw new ArgumentOutOfRangeException ("attribute index out of range.");
378 currentAttribute = i;
379 currentAttributeValue = -1;
380 cursorToken = attributeTokens [i];
383 public override bool MoveToAttribute (string name)
385 for (int i = 0; i < attributeCount; i++) {
386 XmlAttributeTokenInfo ti = attributeTokens [i];
387 if (ti.Name == name) {
395 public override bool MoveToAttribute (string localName, string namespaceName)
397 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
400 MoveToAttribute (idx);
404 public override bool MoveToElement ()
406 if (currentToken == null) // for attribute .ctor()
409 if (cursorToken == currentToken)
412 if (currentAttribute >= 0) {
413 currentAttribute = -1;
414 currentAttributeValue = -1;
415 cursorToken = currentToken;
422 public override bool MoveToFirstAttribute ()
424 if (attributeCount == 0)
427 return MoveToNextAttribute ();
430 public override bool MoveToNextAttribute ()
432 if (currentAttribute == 0 && attributeCount == 0)
434 if (currentAttribute + 1 < attributeCount) {
436 currentAttributeValue = -1;
437 cursorToken = attributeTokens [currentAttribute];
444 public override bool Read ()
446 if (startNodeType == XmlNodeType.Attribute) {
447 if (currentAttribute == 0)
448 return false; // already read.
450 IncrementAttributeToken ();
451 ReadAttributeValueTokens ('"');
452 cursorToken = attributeTokens [0];
453 currentAttributeValue = -1;
454 readState = ReadState.Interactive;
459 readState = ReadState.Interactive;
460 currentLinkedNodeLineNumber = line;
461 currentLinkedNodeLinePosition = column;
462 useProceedingLineInfo = true;
464 cursorToken = currentToken;
466 currentAttribute = currentAttributeValue = -1;
467 currentToken.Clear ();
469 // It was moved from end of ReadStartTag ().
475 if (shouldSkipUntilEndTag) {
476 shouldSkipUntilEndTag = false;
477 return ReadUntilEndTag ();
480 base64CacheStartsAt = -1;
482 more = ReadContent ();
484 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
485 throw new XmlException ("Document element did not appear.");
487 useProceedingLineInfo = false;
491 public override bool ReadAttributeValue ()
493 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
497 if (currentAttribute < 0)
499 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
500 if (currentAttributeValue < 0)
501 currentAttributeValue = ti.ValueTokenStartIndex - 1;
503 if (currentAttributeValue < ti.ValueTokenEndIndex) {
504 currentAttributeValue++;
505 cursorToken = attributeValueTokens [currentAttributeValue];
512 public int ReadBase64 (byte [] buffer, int offset, int length)
515 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
517 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
518 else if (buffer.Length < offset + length)
519 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
521 if (length == 0) // It does not raise an error.
524 int bufIndex = offset;
525 int bufLast = offset + length;
527 if (base64CacheStartsAt >= 0) {
528 for (int i = base64CacheStartsAt; i < 3; i++) {
529 buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
530 if (bufIndex == bufLast)
531 return bufLast - offset;
535 for (int i = 0; i < 3; i++)
537 base64CacheStartsAt = -1;
539 int max = (int) System.Math.Ceiling (4.0 / 3 * length);
540 int additional = max % 4;
542 max += 4 - additional;
543 char [] chars = new char [max];
544 int charsLength = ReadChars (chars, 0, max);
548 for (int i = 0; i < charsLength - 3; i += 4) {
549 b = (byte) (GetBase64Byte (chars [i]) << 2);
550 if (bufIndex < bufLast)
551 buffer [bufIndex] = b;
553 if (base64CacheStartsAt < 0)
554 base64CacheStartsAt = 0;
557 // charsLength mod 4 might not equals to 0.
558 if (i + 1 == charsLength)
560 b = GetBase64Byte (chars [i + 1]);
561 work = (byte) (b >> 4);
562 if (bufIndex < bufLast) {
563 buffer [bufIndex] += work;
567 base64Cache [0] += work;
569 work = (byte) ((b & 0xf) << 4);
570 if (bufIndex < bufLast) {
571 buffer [bufIndex] = work;
574 if (base64CacheStartsAt < 0)
575 base64CacheStartsAt = 1;
576 base64Cache [1] = work;
579 if (i + 2 == charsLength)
581 b = GetBase64Byte (chars [i + 2]);
582 work = (byte) (b >> 2);
583 if (bufIndex < bufLast) {
584 buffer [bufIndex] += work;
588 base64Cache [1] += work;
590 work = (byte) ((b & 3) << 6);
591 if (bufIndex < bufLast)
592 buffer [bufIndex] = work;
594 if (base64CacheStartsAt < 0)
595 base64CacheStartsAt = 2;
596 base64Cache [2] = work;
598 if (i + 3 == charsLength)
600 work = GetBase64Byte (chars [i + 3]);
601 if (bufIndex < bufLast) {
602 buffer [bufIndex] += work;
606 base64Cache [2] += work;
608 return System.Math.Min (bufLast - offset, bufIndex - offset);
611 public int ReadBinHex (byte [] buffer, int offset, int length)
614 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
616 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
617 else if (buffer.Length < offset + length)
618 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
623 char [] chars = new char [length * 2];
624 int charsLength = ReadChars (chars, 0, length * 2);
625 return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
628 public int ReadChars (char [] buffer, int offset, int length)
630 return ReadCharsInternal (buffer, offset, length);
635 public override string ReadInnerXml ()
637 return ReadInnerXmlInternal ();
640 public override string ReadOuterXml ()
642 return ReadOuterXmlInternal ();
645 public override string ReadString ()
647 return ReadStringInternal ();
651 public void ResetState ()
656 public override void ResolveEntity ()
658 // XmlTextReader does not resolve entities.
659 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
663 [MonoTODO ("Implement for performance reason")]
664 public override void Skip ()
672 // Parsed DTD Objects
673 #if DTD_HANDLE_EVENTS
674 internal event ValidationEventHandler ValidationEventHandler;
677 internal DTDObjectModel DTD {
678 get { return parserContext.Dtd; }
681 internal XmlResolver Resolver {
682 get { return resolver; }
687 internal class XmlTokenInfo
689 public XmlTokenInfo (XmlTextReader xtr, bool isPrimaryToken)
691 this.isPrimaryToken = isPrimaryToken;
699 protected XmlTextReader Reader;
702 public string LocalName;
703 public string Prefix;
704 public string NamespaceURI;
705 public bool IsEmptyElement;
706 public char QuoteChar;
707 public int LineNumber;
708 public int LinePosition;
710 public XmlNodeType NodeType;
712 public virtual string Value {
714 if (valueCache != null)
717 case XmlNodeType.Text:
718 case XmlNodeType.SignificantWhitespace:
719 case XmlNodeType.Whitespace:
720 case XmlNodeType.Comment:
721 case XmlNodeType.CDATA:
722 case XmlNodeType.ProcessingInstruction:
723 valueCache = Reader.CreateValueString ();
728 set { valueCache = value; }
731 public virtual void Clear ()
734 NodeType = XmlNodeType.None;
735 Name = LocalName = Prefix = NamespaceURI = String.Empty;
736 IsEmptyElement = false;
738 LineNumber = LinePosition = 0;
741 internal virtual void FillNames ()
743 if (Reader.Namespaces) {
744 int indexOfColon = -1;
746 case XmlNodeType.Attribute:
747 case XmlNodeType.Element:
748 case XmlNodeType.EndElement:
749 indexOfColon = Name.IndexOf (':');
753 if (indexOfColon == -1) {
754 Prefix = String.Empty;
757 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
758 // However, this might be reverted if NameTable is got improved.
759 char [] nameArr = Name.ToCharArray ();
760 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
761 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
762 // Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
763 // LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
768 case XmlNodeType.Attribute:
769 if (Prefix.Length == 0)
770 NamespaceURI = string.Empty;
772 NamespaceURI = Reader.LookupNamespace (Prefix, true);
775 case XmlNodeType.Element:
776 case XmlNodeType.EndElement:
777 NamespaceURI = Reader.LookupNamespace (Prefix, true);
784 Prefix = String.Empty;
790 internal class XmlAttributeTokenInfo : XmlTokenInfo
792 public XmlAttributeTokenInfo (XmlTextReader reader)
793 : base (reader, false)
795 NodeType = XmlNodeType.Attribute;
798 public int ValueTokenStartIndex;
799 public int ValueTokenEndIndex;
801 bool cachedNormalization;
802 StringBuilder tmpBuilder = new StringBuilder ();
804 public override string Value {
806 if (cachedNormalization != Reader.Normalization)
808 if (valueCache != null)
811 cachedNormalization = Reader.Normalization;
813 // An empty value should return String.Empty.
814 if (ValueTokenStartIndex == ValueTokenEndIndex) {
815 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
816 if (ti.NodeType == XmlNodeType.EntityReference)
817 valueCache = String.Concat ("&", ti.Name, ";");
819 valueCache = ti.Value;
820 if (cachedNormalization)
825 tmpBuilder.Length = 0;
826 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
827 XmlTokenInfo ti = Reader.attributeValueTokens [i];
828 if (ti.NodeType == XmlNodeType.Text)
829 tmpBuilder.Append (ti.Value);
831 tmpBuilder.Append ('&');
832 tmpBuilder.Append (ti.Name);
833 tmpBuilder.Append (';');
837 valueCache = tmpBuilder.ToString ();
838 if (cachedNormalization)
843 set { valueCache = value; }
846 public override void Clear ()
850 NodeType = XmlNodeType.Attribute;
851 ValueTokenStartIndex = ValueTokenEndIndex = 0;
854 internal override void FillNames ()
857 if (Prefix == "xmlns" || Name == "xmlns")
858 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
861 private void NormalizeSpaces ()
863 tmpBuilder.Length = 0;
864 for (int i = 0; i < valueCache.Length; i++)
865 switch (valueCache [i]) {
867 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
872 tmpBuilder.Append (' ');
875 tmpBuilder.Append (valueCache [i]);
878 valueCache = tmpBuilder.ToString ();
882 private XmlTokenInfo cursorToken;
883 private XmlTokenInfo currentToken;
884 private XmlAttributeTokenInfo currentAttributeToken;
885 private XmlTokenInfo currentAttributeValueToken;
886 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
887 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
888 private int currentAttribute;
889 private int currentAttributeValue;
890 private int attributeCount;
892 private XmlParserContext parserContext;
894 private ReadState readState;
897 private int elementDepth;
898 private bool depthUp;
900 private bool popScope;
902 private string [] elementNames;
903 int elementNameStackPos;
905 private bool allowMultipleRoot;
907 private bool isStandalone;
909 private bool returnEntityReference;
910 private string entityReferenceName;
912 private char [] nameBuffer;
913 private int nameLength;
914 private int nameCapacity;
915 private const int initialNameCapacity = 32;
917 private char [] valueBuffer;
918 private int valueLength;
919 private int valueCapacity;
920 private const int initialValueCapacity = 256;
922 private char [] currentTagBuffer;
923 private int currentTagLength;
924 private int currentTagCapacity;
925 private const int initialCurrentTagCapacity = 256;
927 private TextReader reader;
928 private char [] peekChars;
929 private int peekCharsIndex;
930 private int peekCharsLength;
931 private const int peekCharCapacity = 1024;
936 private int currentLinkedNodeLineNumber;
937 private int currentLinkedNodeLinePosition;
938 private bool useProceedingLineInfo;
940 private XmlNodeType startNodeType;
941 // State machine attribute.
942 // XmlDeclaration: after the first node.
943 // DocumentType: after doctypedecl
944 // Element: inside document element
945 // EndElement: after document element
946 private XmlNodeType currentState;
948 // For ReadChars()/ReadBase64()/ReadBinHex()
949 private bool shouldSkipUntilEndTag;
950 private byte [] base64Cache = new byte [3];
951 private int base64CacheStartsAt;
953 // These values are never re-initialized.
954 private bool namespaces = true;
955 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
956 private XmlResolver resolver = new XmlUrlResolver ();
957 private bool normalization = false;
959 private bool prohibitDtd = false;
964 currentToken = new XmlTokenInfo (this, true);
965 cursorToken = currentToken;
966 currentAttribute = -1;
967 currentAttributeValue = -1;
970 readState = ReadState.Initial;
971 allowMultipleRoot = false;
977 popScope = allowMultipleRoot = false;
978 elementNames = new string [10];
979 elementNameStackPos = 0;
981 isStandalone = false;
982 returnEntityReference = false;
983 entityReferenceName = String.Empty;
985 nameBuffer = new char [initialNameCapacity];
987 nameCapacity = initialNameCapacity;
989 valueBuffer = new char [initialValueCapacity];
991 valueCapacity = initialValueCapacity;
993 currentTagBuffer = new char [initialCurrentTagCapacity];
994 currentTagLength = 0;
995 currentTagCapacity = initialCurrentTagCapacity;
999 if (peekChars == null)
1000 peekChars = new char [peekCharCapacity];
1004 currentTagLength = 0;
1006 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
1007 useProceedingLineInfo = false;
1009 currentState = XmlNodeType.None;
1011 shouldSkipUntilEndTag = false;
1012 base64CacheStartsAt = -1;
1015 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1017 startNodeType = fragType;
1018 parserContext = context;
1019 if (context == null) {
1020 XmlNameTable nt = new NameTable ();
1021 parserContext = new XmlParserContext (nt,
1022 new XmlNamespaceManager (nt),
1027 if (url != null && url.Length > 0) {
1030 uri = new Uri (url);
1031 } catch (Exception) {
1032 string path = Path.GetFullPath ("./a");
1033 uri = new Uri (new Uri (path), url);
1035 parserContext.BaseURI = uri.ToString ();
1041 case XmlNodeType.Attribute:
1042 fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1044 case XmlNodeType.Element:
1045 currentState = XmlNodeType.Element;
1046 allowMultipleRoot = true;
1048 case XmlNodeType.Document:
1051 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1057 // Use this method rather than setting the properties
1058 // directly so that all the necessary properties can
1059 // be changed in harmony with each other. Maybe the
1060 // fields should be in a seperate class to help enforce
1062 private void SetProperties (
1063 XmlNodeType nodeType,
1065 bool isEmptyElement,
1067 bool clearAttributes)
1069 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1070 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1071 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1074 private void SetProperties (
1076 XmlNodeType nodeType,
1078 bool isEmptyElement,
1080 bool clearAttributes)
1083 token.NodeType = nodeType;
1085 token.IsEmptyElement = isEmptyElement;
1086 token.Value = value;
1087 this.elementDepth = depth;
1089 if (clearAttributes)
1095 private void ClearAttributes ()
1097 for (int i = 0; i < attributeCount; i++)
1098 attributeTokens [i].Clear ();
1100 currentAttribute = -1;
1101 currentAttributeValue = -1;
1104 private int PeekChar ()
1106 if (peekCharsLength == peekCharsIndex) {
1107 if (!ReadTextReader ())
1112 return peekChars [peekCharsIndex];
1115 private int ReadChar ()
1119 if (peekCharsLength == peekCharsIndex) {
1120 if (!ReadTextReader ())
1124 ch = peekChars [peekCharsIndex++];
1132 if (currentState != XmlNodeType.Element)
1133 AppendCurrentTagChar (ch);
1137 private bool ReadTextReader ()
1140 peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity);
1141 if (peekCharsLength == 0)
1146 private string ExpandSurrogateChar (int ch)
1148 if (ch < Char.MaxValue)
1149 return ((char) ch).ToString ();
1151 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1152 return new string (tmp);
1156 // This should really keep track of some state so
1157 // that it's not possible to have more than one document
1158 // element or text outside of the document element.
1159 private bool ReadContent ()
1161 currentTagLength = 0;
1163 parserContext.NamespaceManager.PopScope ();
1167 if (returnEntityReference)
1168 SetEntityReferenceProperties ();
1170 switch (PeekChar ()) {
1175 case '\r': goto case ' ';
1176 case '\n': goto case ' ';
1177 case '\t': goto case ' ';
1179 if (whitespaceHandling == WhitespaceHandling.All ||
1180 whitespaceHandling == WhitespaceHandling.Significant)
1184 return ReadContent ();
1188 readState = ReadState.EndOfFile;
1189 ClearValueBuffer ();
1191 XmlNodeType.None, // nodeType
1192 String.Empty, // name
1193 false, // isEmptyElement
1195 true // clearAttributes
1198 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1206 return this.ReadState != ReadState.EndOfFile;
1209 private void SetEntityReferenceProperties ()
1211 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1212 if (this.isStandalone)
1213 if (DTD == null || decl == null || !decl.IsInternalSubset)
1214 throw new XmlException (this as IXmlLineInfo,
1215 "Standalone document must not contain any references to an non-internally declared entity.");
1216 if (decl != null && decl.NotationName != null)
1217 throw new XmlException (this as IXmlLineInfo,
1218 "Reference to any unparsed entities is not allowed here.");
1220 ClearValueBuffer ();
1222 XmlNodeType.EntityReference, // nodeType
1223 entityReferenceName, // name
1224 false, // isEmptyElement
1226 true // clearAttributes
1229 returnEntityReference = false;
1230 entityReferenceName = String.Empty;
1233 // The leading '<' has already been consumed.
1234 private void ReadTag ()
1236 switch (PeekChar ())
1244 ReadProcessingInstruction ();
1256 // The leading '<' has already been consumed.
1257 private void ReadStartTag ()
1259 if (currentState == XmlNodeType.EndElement)
1260 throw new XmlException (this as IXmlLineInfo,
1261 "Element cannot appear in this state.");
1262 currentState = XmlNodeType.Element;
1264 parserContext.NamespaceManager.PushScope ();
1266 string name = ReadName ();
1267 if (currentState == XmlNodeType.EndElement)
1268 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1270 bool isEmptyElement = false;
1275 if (XmlChar.IsFirstNameChar (PeekChar ()))
1276 ReadAttributes (false);
1277 cursorToken = this.currentToken;
1280 for (int i = 0; i < attributeCount; i++)
1281 attributeTokens [i].FillNames ();
1284 for (int i = 0; i < attributeCount; i++)
1285 for (int j = i + 1; j < attributeCount; j++)
1286 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1287 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1288 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1289 throw new XmlException (this as IXmlLineInfo,
1290 "Attribute name and qualified name must be identical.");
1292 string baseUri = GetAttribute ("xml:base");
1293 if (baseUri != null) {
1294 if (this.resolver != null)
1295 parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1297 parserContext.BaseURI = baseUri;
1299 string xmlLang = GetAttribute ("xml:lang");
1300 if (xmlLang != null)
1301 parserContext.XmlLang = xmlLang;
1302 string xmlSpaceAttr = GetAttribute ("xml:space");
1303 if (xmlSpaceAttr != null) {
1304 if (xmlSpaceAttr == "preserve")
1305 parserContext.XmlSpace = XmlSpace.Preserve;
1306 else if (xmlSpaceAttr == "default")
1307 parserContext.XmlSpace = XmlSpace.Default;
1309 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1311 if (PeekChar () == '/') {
1313 isEmptyElement = true;
1318 PushElementName (name);
1319 parserContext.PushScope ();
1324 XmlNodeType.Element, // nodeType
1326 isEmptyElement, // isEmptyElement
1328 false // clearAttributes
1332 CheckCurrentStateUpdate ();
1335 private void PushElementName (string name)
1337 if (elementNames.Length == elementNameStackPos) {
1338 string [] newArray = new string [elementNames.Length * 2];
1339 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1340 elementNames = newArray;
1342 elementNames [elementNameStackPos++] = name;
1345 // The reader is positioned on the first character
1346 // of the element's name.
1347 private void ReadEndTag ()
1349 if (currentState != XmlNodeType.Element)
1350 throw new XmlException (this as IXmlLineInfo,
1351 "End tag cannot appear in this state.");
1353 string name = ReadName ();
1354 if (elementNameStackPos == 0)
1355 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1356 string expected = elementNames [--elementNameStackPos];
1357 if (expected != name)
1358 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1359 parserContext.PopScope ();
1361 ExpectAfterWhitespace ('>');
1366 XmlNodeType.EndElement, // nodeType
1368 false, // isEmptyElement
1370 true // clearAttributes
1375 CheckCurrentStateUpdate ();
1378 private void CheckCurrentStateUpdate ()
1380 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1381 currentState = XmlNodeType.EndElement;
1384 private void AppendNameChar (int ch)
1386 if (nameLength == nameCapacity)
1387 ExpandNameCapacity ();
1388 if (ch < Char.MaxValue)
1389 nameBuffer [nameLength++] = (char) ch;
1391 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1392 if (nameLength == nameCapacity)
1393 ExpandNameCapacity ();
1394 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1398 private void ExpandNameCapacity ()
1400 nameCapacity = nameCapacity * 2;
1401 char [] oldNameBuffer = nameBuffer;
1402 nameBuffer = new char [nameCapacity];
1403 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1406 private string CreateNameString ()
1408 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1411 private void AppendValueChar (int ch)
1413 if (valueLength == valueCapacity)
1414 ExpandValueCapacity ();
1415 if (ch < Char.MaxValue)
1416 valueBuffer [valueLength++] = (char) ch;
1418 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1419 if (valueLength == valueCapacity)
1420 ExpandValueCapacity ();
1421 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1425 private void ExpandValueCapacity ()
1427 valueCapacity = valueCapacity * 2;
1428 char [] oldValueBuffer = valueBuffer;
1429 valueBuffer = new char [valueCapacity];
1430 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1433 private string CreateValueString ()
1435 return new string (valueBuffer, 0, valueLength);
1438 private void ClearValueBuffer ()
1443 private void AppendCurrentTagChar (int ch)
1445 if (currentTagLength == currentTagCapacity)
1446 ExpandCurrentTagCapacity ();
1447 if (ch < Char.MaxValue)
1448 currentTagBuffer [currentTagLength++] = (char) ch;
1450 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1451 if (currentTagLength == currentTagCapacity)
1452 ExpandCurrentTagCapacity ();
1453 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1457 private void ExpandCurrentTagCapacity ()
1459 currentTagCapacity = currentTagCapacity * 2;
1460 char [] oldCurrentTagBuffer = currentTagBuffer;
1461 currentTagBuffer = new char [currentTagCapacity];
1462 Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1465 private string CreateCurrentTagString ()
1467 return new string (currentTagBuffer, 0, currentTagLength);
1470 private void ClearCurrentTagBuffer ()
1472 currentTagLength = 0;
1475 // The reader is positioned on the first character
1477 private void ReadText (bool notWhitespace)
1479 if (currentState != XmlNodeType.Element)
1480 throw new XmlException (this as IXmlLineInfo,
1481 "Text node cannot appear in this state.");
1484 ClearValueBuffer ();
1486 int ch = PeekChar ();
1487 bool previousWasCloseBracket = false;
1489 while (ch != '<' && ch != -1) {
1492 ch = ReadReference (false);
1493 if (returnEntityReference) // Returns -1 if char validation should not be done
1499 if (normalization && XmlChar.IsInvalid (ch))
1500 throw new XmlException (this, "Not allowed character was found.");
1501 AppendValueChar (ch);
1505 if (previousWasCloseBracket)
1506 if (PeekChar () == '>')
1507 throw new XmlException (this as IXmlLineInfo,
1508 "Inside text content, character sequence ']]>' is not allowed.");
1509 previousWasCloseBracket = true;
1511 else if (previousWasCloseBracket)
1512 previousWasCloseBracket = false;
1514 notWhitespace = true;
1517 if (returnEntityReference && valueLength == 0) {
1518 SetEntityReferenceProperties ();
1520 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1521 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1523 nodeType, // nodeType
1524 String.Empty, // name
1525 false, // isEmptyElement
1526 null, // value: create only when required
1527 true // clearAttributes
1532 // The leading '&' has already been consumed.
1533 // Returns true if the entity reference isn't a simple
1534 // character reference or one of the predefined entities.
1535 // This allows the ReadText method to break so that the
1536 // next call to Read will return the EntityReference node.
1537 private int ReadReference (bool ignoreEntityReferences)
1539 if (PeekChar () == '#') {
1541 return ReadCharacterReference ();
1543 return ReadEntityReference (ignoreEntityReferences);
1546 private int ReadCharacterReference ()
1550 if (PeekChar () == 'x') {
1553 while (PeekChar () != ';' && PeekChar () != -1) {
1554 int ch = ReadChar ();
1556 if (ch >= '0' && ch <= '9')
1557 value = (value << 4) + ch - '0';
1558 else if (ch >= 'A' && ch <= 'F')
1559 value = (value << 4) + ch - 'A' + 10;
1560 else if (ch >= 'a' && ch <= 'f')
1561 value = (value << 4) + ch - 'a' + 10;
1563 throw new XmlException (this as IXmlLineInfo,
1565 "invalid hexadecimal digit: {0} (#x{1:X})",
1570 while (PeekChar () != ';' && PeekChar () != -1) {
1571 int ch = ReadChar ();
1573 if (ch >= '0' && ch <= '9')
1574 value = value * 10 + ch - '0';
1576 throw new XmlException (this as IXmlLineInfo,
1578 "invalid decimal digit: {0} (#x{1:X})",
1586 // There is no way to save surrogate pairs...
1587 if (normalization && XmlChar.IsInvalid (value))
1588 throw new XmlException (this as IXmlLineInfo,
1589 "Referenced character was not allowed in XML.");
1593 // Returns -1 if it should not be validated.
1594 // Real EOF must not be detected here.
1595 private int ReadEntityReference (bool ignoreEntityReferences)
1597 string name = ReadName ();
1600 int predefined = XmlChar.GetPredefinedEntity (name);
1601 if (predefined >= 0)
1604 if (ignoreEntityReferences) {
1605 AppendValueChar ('&');
1606 for (int i = 0; i < name.Length; i++)
1607 AppendValueChar (name [i]);
1608 AppendValueChar (';');
1610 returnEntityReference = true;
1611 entityReferenceName = name;
1617 // The reader is positioned on the first character of
1618 // the attribute name.
1619 private void ReadAttributes (bool isXmlDecl)
1622 bool requireWhitespace = false;
1623 currentAttribute = -1;
1624 currentAttributeValue = -1;
1627 if (!SkipWhitespace () && requireWhitespace)
1628 throw new XmlException ("Unexpected token. Name is required here.");
1630 IncrementAttributeToken ();
1631 currentAttributeToken.LineNumber = line;
1632 currentAttributeToken.LinePosition = column;
1634 currentAttributeToken.LocalName =
1635 currentAttributeToken.Name = ReadName ();
1636 ExpectAfterWhitespace ('=');
1638 ReadAttributeValueTokens (-1);
1641 if (currentAttributeToken.Name == "xmlns")
1642 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1643 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1644 string nsPrefix = currentAttributeToken.Name.Substring (6);
1645 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1648 if (!SkipWhitespace ())
1649 requireWhitespace = true;
1650 peekChar = PeekChar ();
1652 if (peekChar == '?')
1655 else if (peekChar == '/' || peekChar == '>')
1657 } while (peekChar != -1);
1659 currentAttribute = -1;
1660 currentAttributeValue = -1;
1663 private void AddAttribute (string name, string value)
1665 IncrementAttributeToken ();
1666 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1667 ati.Name = "SYSTEM";
1669 IncrementAttributeValueToken ();
1670 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1672 SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1676 private void IncrementAttributeToken ()
1679 if (attributeTokens.Length == currentAttribute) {
1680 XmlAttributeTokenInfo [] newArray =
1681 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1682 attributeTokens.CopyTo (newArray, 0);
1683 attributeTokens = newArray;
1685 if (attributeTokens [currentAttribute] == null)
1686 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1687 currentAttributeToken = attributeTokens [currentAttribute];
1688 currentAttributeToken.Clear ();
1691 private void IncrementAttributeValueToken ()
1693 ClearValueBuffer ();
1694 currentAttributeValue++;
1695 if (attributeValueTokens.Length == currentAttributeValue) {
1696 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1697 attributeValueTokens.CopyTo (newArray, 0);
1698 attributeValueTokens = newArray;
1700 if (attributeValueTokens [currentAttributeValue] == null)
1701 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1702 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1703 currentAttributeValueToken.Clear ();
1706 // LAMESPEC: Orthodox XML reader should normalize attribute values
1707 private void ReadAttributeValueTokens (int dummyQuoteChar)
1709 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1711 if (quoteChar != '\'' && quoteChar != '\"')
1712 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1713 currentAttributeToken.QuoteChar = (char) quoteChar;
1715 IncrementAttributeValueToken ();
1716 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1717 currentAttributeValueToken.LineNumber = line;
1718 currentAttributeValueToken.LinePosition = column;
1720 bool incrementToken = false;
1721 bool isNewToken = true;
1726 if (ch == quoteChar)
1729 if (incrementToken) {
1730 IncrementAttributeValueToken ();
1731 currentAttributeValueToken.LineNumber = line;
1732 currentAttributeValueToken.LinePosition = column;
1733 incrementToken = false;
1740 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1742 if (dummyQuoteChar < 0)
1743 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1744 else // Attribute value constructor.
1748 int startPosition = currentTagLength - 1;
1749 if (PeekChar () == '#') {
1751 ch = ReadCharacterReference ();
1752 if (normalization && XmlChar.IsInvalid (ch))
1753 throw new XmlException (this as IXmlLineInfo,
1754 "Not allowed character was found.");
1755 AppendValueChar (ch);
1758 // Check XML 1.0 section 3.1 WFC.
1759 string entName = ReadName ();
1761 int predefined = XmlChar.GetPredefinedEntity (entName);
1762 if (predefined < 0) {
1763 CheckAttributeEntityReferenceWFC (entName);
1764 currentAttributeValueToken.Value = CreateValueString ();
1765 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1767 IncrementAttributeValueToken ();
1768 currentAttributeValueToken.Name = entName;
1769 currentAttributeValueToken.Value = String.Empty;
1770 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1771 incrementToken = true;
1774 AppendValueChar (predefined);
1777 if (normalization && XmlChar.IsInvalid (ch))
1778 throw new XmlException (this, "Invalid character was found.");
1779 AppendValueChar (ch);
1785 if (!incrementToken) {
1786 currentAttributeValueToken.Value = CreateValueString ();
1787 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1789 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1793 private void CheckAttributeEntityReferenceWFC (string entName)
1795 DTDEntityDeclaration entDecl =
1796 DTD == null ? null : DTD.EntityDecls [entName];
1797 if (DTD != null && resolver != null && entDecl == null)
1798 throw new XmlException (this, "Referenced entity does not exist.");
1800 if (entDecl == null)
1803 if (entDecl.HasExternalReference)
1804 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
1805 if (isStandalone && !entDecl.IsInternalSubset)
1806 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
1807 if (entDecl.EntityValue.IndexOf ('<') >= 0)
1808 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1811 // The reader is positioned on the first character
1814 // It may be xml declaration or processing instruction.
1815 private void ReadProcessingInstruction ()
1817 string target = ReadName ();
1818 if (target == "xml") {
1819 ReadXmlDeclaration ();
1821 } else if (target.ToLower () == "xml")
1822 throw new XmlException (this as IXmlLineInfo,
1823 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1825 if (currentState == XmlNodeType.None)
1826 currentState = XmlNodeType.XmlDeclaration;
1828 if (!SkipWhitespace ())
1829 if (PeekChar () != '?')
1830 throw new XmlException (this as IXmlLineInfo,
1831 "Invalid processing instruction name was found.");
1833 ClearValueBuffer ();
1835 while (PeekChar () != -1) {
1836 int ch = ReadChar ();
1838 if (ch == '?' && PeekChar () == '>') {
1843 if (normalization && XmlChar.IsInvalid (ch))
1844 throw new XmlException (this, "Invalid character was found.");
1845 AppendValueChar (ch);
1849 XmlNodeType.ProcessingInstruction, // nodeType
1851 false, // isEmptyElement
1852 null, // value: create only when required
1853 true // clearAttributes
1857 // The reader is positioned after "<?xml "
1858 private void ReadXmlDeclaration ()
1860 if (currentState != XmlNodeType.None) {
1861 throw new XmlException (this as IXmlLineInfo,
1862 "XML declaration cannot appear in this state.");
1864 currentState = XmlNodeType.XmlDeclaration;
1868 ReadAttributes (true); // They must have "version."
1869 string version = GetAttribute ("version");
1871 string message = null;
1873 if (attributeTokens [0].Name != "version" || version != "1.0")
1874 message = "Version 1.0 declaration is required in XML Declaration.";
1875 else if (attributeCount > 1 &&
1876 (attributeTokens [1].Name != "encoding" &&
1877 attributeTokens [1].Name != "standalone"))
1878 message = "Invalid Xml Declaration markup was found.";
1879 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1880 message = "Invalid Xml Declaration markup was found.";
1881 string sa = GetAttribute ("standalone");
1882 if (sa != null && sa != "yes" && sa != "no")
1883 message = "Only 'yes' or 'no' is allowed for standalone.";
1885 this.isStandalone = (sa == "yes");
1887 if (message != null)
1888 throw new XmlException (this as IXmlLineInfo, message);
1891 XmlNodeType.XmlDeclaration, // nodeType
1893 false, // isEmptyElement
1894 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1895 false // clearAttributes
1901 internal void SkipTextDeclaration ()
1903 this.currentState = XmlNodeType.Element;
1905 if (PeekChar () != '<')
1910 if (PeekChar () != '?') {
1916 while (peekCharsIndex < 6) {
1917 if (PeekChar () < 0)
1922 if (new string (peekChars, 2, 4) != "xml ") {
1923 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1924 throw new XmlException (this as IXmlLineInfo,
1925 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1934 if (PeekChar () == 'v') {
1936 ExpectAfterWhitespace ('=');
1938 int quoteChar = ReadChar ();
1939 char [] expect1_0 = new char [3];
1940 int versionLength = 0;
1941 switch (quoteChar) {
1944 while (PeekChar () != quoteChar) {
1945 if (PeekChar () == -1)
1946 throw new XmlException (this as IXmlLineInfo,
1947 "Invalid version declaration inside text declaration.");
1948 else if (versionLength == 3)
1949 throw new XmlException (this as IXmlLineInfo,
1950 "Invalid version number inside text declaration.");
1952 expect1_0 [versionLength] = (char) ReadChar ();
1954 if (versionLength == 3 && new String (expect1_0) != "1.0")
1955 throw new XmlException (this as IXmlLineInfo,
1956 "Invalid version number inside text declaration.");
1963 throw new XmlException (this as IXmlLineInfo,
1964 "Invalid version declaration inside text declaration.");
1968 if (PeekChar () == 'e') {
1969 Expect ("encoding");
1970 ExpectAfterWhitespace ('=');
1972 int quoteChar = ReadChar ();
1973 switch (quoteChar) {
1976 while (PeekChar () != quoteChar)
1977 if (ReadChar () == -1)
1978 throw new XmlException (this as IXmlLineInfo,
1979 "Invalid encoding declaration inside text declaration.");
1984 throw new XmlException (this as IXmlLineInfo,
1985 "Invalid encoding declaration inside text declaration.");
1987 // Encoding value should be checked inside XmlInputStream.
1990 throw new XmlException (this as IXmlLineInfo,
1991 "Encoding declaration is mandatory in text declaration.");
1996 // The reader is positioned on the first character after
1997 // the leading '<!'.
1998 private void ReadDeclaration ()
2000 int ch = PeekChar ();
2018 throw new XmlException (this as IXmlLineInfo,
2019 "Unexpected declaration markup was found.");
2023 // The reader is positioned on the first character after
2024 // the leading '<!--'.
2025 private void ReadComment ()
2027 if (currentState == XmlNodeType.None)
2028 currentState = XmlNodeType.XmlDeclaration;
2030 ClearValueBuffer ();
2032 while (PeekChar () != -1) {
2033 int ch = ReadChar ();
2035 if (ch == '-' && PeekChar () == '-') {
2038 if (PeekChar () != '>')
2039 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2045 if (XmlChar.IsInvalid (ch))
2046 throw new XmlException (this as IXmlLineInfo,
2047 "Not allowed character was found.");
2049 AppendValueChar (ch);
2053 XmlNodeType.Comment, // nodeType
2054 String.Empty, // name
2055 false, // isEmptyElement
2056 null, // value: create only when required
2057 true // clearAttributes
2061 // The reader is positioned on the first character after
2062 // the leading '<![CDATA['.
2063 private void ReadCDATA ()
2065 if (currentState != XmlNodeType.Element)
2066 throw new XmlException (this as IXmlLineInfo,
2067 "CDATA section cannot appear in this state.");
2069 ClearValueBuffer ();
2073 while (PeekChar () != -1) {
2078 if (ch == ']' && PeekChar () == ']') {
2079 ch = ReadChar (); // ']'
2081 if (PeekChar () == '>') {
2088 if (normalization && XmlChar.IsInvalid (ch))
2089 throw new XmlException (this, "Invalid character was found.");
2091 AppendValueChar (ch);
2095 XmlNodeType.CDATA, // nodeType
2096 String.Empty, // name
2097 false, // isEmptyElement
2098 null, // value: create only when required
2099 true // clearAttributes
2103 // The reader is positioned on the first character after
2104 // the leading '<!DOCTYPE'.
2105 private void ReadDoctypeDecl ()
2107 switch (currentState) {
2108 case XmlNodeType.DocumentType:
2109 case XmlNodeType.Element:
2110 case XmlNodeType.EndElement:
2111 throw new XmlException (this as IXmlLineInfo,
2112 "Document type cannot appear in this state.");
2114 currentState = XmlNodeType.DocumentType;
2116 string doctypeName = null;
2117 string publicId = null;
2118 string systemId = null;
2119 int intSubsetStartLine = 0;
2120 int intSubsetStartColumn = 0;
2123 doctypeName = ReadName ();
2128 systemId = ReadSystemLiteral (true);
2131 publicId = ReadPubidLiteral ();
2132 if (!SkipWhitespace ())
2133 throw new XmlException (this as IXmlLineInfo,
2134 "Whitespace is required between PUBLIC id and SYSTEM id.");
2135 systemId = ReadSystemLiteral (false);
2141 if(PeekChar () == '[')
2143 // read markupdecl etc. or end of decl
2145 intSubsetStartLine = this.LineNumber;
2146 intSubsetStartColumn = this.LinePosition;
2147 int startPos = currentTagLength;
2148 ReadInternalSubset ();
2149 int endPos = currentTagLength - 1;
2150 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2152 // end of DOCTYPE decl.
2153 ExpectAfterWhitespace ('>');
2155 GenerateDTDObjectModel (doctypeName, publicId,
2156 systemId, parserContext.InternalSubset,
2157 intSubsetStartLine, intSubsetStartColumn);
2159 // set properties for <!DOCTYPE> node
2161 XmlNodeType.DocumentType, // nodeType
2162 doctypeName, // name
2163 false, // isEmptyElement
2164 parserContext.InternalSubset, // value
2165 true // clearAttributes
2168 if (publicId != null)
2169 AddAttribute ("PUBLIC", publicId);
2170 if (systemId != null)
2171 AddAttribute ("SYSTEM", systemId);
2172 currentAttribute = currentAttributeValue = -1;
2175 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2176 string systemId, string internalSubset)
2178 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2181 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2182 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2185 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2186 DTD.BaseURI = BaseURI;
2188 DTD.PublicId = publicId;
2189 DTD.SystemId = systemId;
2190 DTD.InternalSubset = internalSubset;
2191 DTD.XmlResolver = resolver;
2192 DTD.IsStandalone = isStandalone;
2193 DTD.LineNumber = line;
2194 DTD.LinePosition = column;
2196 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2197 dr.Normalization = this.normalization;
2198 #if DTD_HANDLE_EVENTS
2199 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2201 return dr.GenerateDTDObjectModel ();
2204 private void OnValidationEvent (object o, ValidationEventArgs e)
2206 #if DTD_HANDLE_EVENTS
2207 if (ValidationEventHandler != null)
2208 // Override object as this.
2209 ValidationEventHandler (this, e);
2213 private enum DtdInputState
2226 private class DtdInputStateStack
2228 Stack intern = new Stack ();
2229 public DtdInputStateStack ()
2231 Push (DtdInputState.Free);
2234 public DtdInputState Peek ()
2236 return (DtdInputState) intern.Peek ();
2239 public DtdInputState Pop ()
2241 return (DtdInputState) intern.Pop ();
2244 public void Push (DtdInputState val)
2251 DtdInputStateStack stateStack = new DtdInputStateStack ();
2252 DtdInputState State {
2253 get { return stateStack.Peek (); }
2256 // Simply read but not generate any result.
2257 private void ReadInternalSubset ()
2259 bool continueParse = true;
2261 while (continueParse) {
2262 switch (ReadChar ()) {
2265 case DtdInputState.Free:
2266 continueParse = false;
2268 case DtdInputState.InsideDoubleQuoted:
2270 case DtdInputState.InsideSingleQuoted:
2273 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2277 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2279 if (State == DtdInputState.InsideDoubleQuoted ||
2280 State == DtdInputState.InsideSingleQuoted)
2281 continue; // well-formed
2282 switch (ReadChar ()) {
2284 stateStack.Push (DtdInputState.PI);
2287 switch (ReadChar ()) {
2289 switch (ReadChar ()) {
2292 stateStack.Push (DtdInputState.ElementDecl);
2296 stateStack.Push (DtdInputState.EntityDecl);
2299 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2304 stateStack.Push (DtdInputState.AttlistDecl);
2308 stateStack.Push (DtdInputState.NotationDecl);
2312 stateStack.Push (DtdInputState.Comment);
2317 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2321 if (State == DtdInputState.InsideSingleQuoted)
2323 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2324 stateStack.Push (DtdInputState.InsideSingleQuoted);
2327 if (State == DtdInputState.InsideDoubleQuoted)
2329 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2330 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2334 case DtdInputState.ElementDecl:
2335 goto case DtdInputState.NotationDecl;
2336 case DtdInputState.AttlistDecl:
2337 goto case DtdInputState.NotationDecl;
2338 case DtdInputState.EntityDecl:
2339 goto case DtdInputState.NotationDecl;
2340 case DtdInputState.NotationDecl:
2343 case DtdInputState.InsideDoubleQuoted:
2345 case DtdInputState.InsideSingleQuoted:
2346 continue; // well-formed
2347 case DtdInputState.Comment:
2350 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2354 if (State == DtdInputState.PI) {
2355 if (ReadChar () == '>')
2360 if (State == DtdInputState.Comment) {
2361 if (PeekChar () == '-') {
2369 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2370 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2376 // The reader is positioned on the first 'S' of "SYSTEM".
2377 private string ReadSystemLiteral (bool expectSYSTEM)
2381 if (!SkipWhitespace ())
2382 throw new XmlException (this as IXmlLineInfo,
2383 "Whitespace is required after 'SYSTEM'.");
2387 int quoteChar = ReadChar (); // apos or quot
2388 int startPos = currentTagLength;
2390 ClearValueBuffer ();
2391 while (c != quoteChar) {
2394 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2396 AppendValueChar (c);
2398 return CreateValueString ();
2401 private string ReadPubidLiteral()
2404 if (!SkipWhitespace ())
2405 throw new XmlException (this as IXmlLineInfo,
2406 "Whitespace is required after 'PUBLIC'.");
2407 int quoteChar = ReadChar ();
2408 int startPos = currentTagLength;
2410 ClearValueBuffer ();
2411 while(c != quoteChar)
2414 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2415 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2416 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2418 AppendValueChar (c);
2420 return CreateValueString ();
2423 // The reader is positioned on the first character
2425 private string ReadName ()
2427 int ch = PeekChar ();
2428 if (!XmlChar.IsFirstNameChar (ch))
2429 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2433 AppendNameChar (ReadChar ());
2435 while (XmlChar.IsNameChar (PeekChar ())) {
2436 AppendNameChar (ReadChar ());
2439 return CreateNameString ();
2442 // Read the next character and compare it against the
2443 // specified character.
2444 private void Expect (int expected)
2446 int ch = ReadChar ();
2448 if (ch != expected) {
2449 throw new XmlException (this as IXmlLineInfo,
2451 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2459 private void Expect (string expected)
2461 int len = expected.Length;
2462 for(int i=0; i< len; i++)
2463 Expect (expected[i]);
2466 private void ExpectAfterWhitespace (char c)
2469 int i = ReadChar ();
2470 if (i < 0x21 && XmlChar.IsWhitespace (i))
2473 throw new XmlException (this, String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2478 // Does not consume the first non-whitespace character.
2479 private bool SkipWhitespace ()
2481 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2484 while (XmlChar.IsWhitespace (PeekChar ()))
2489 private void ReadWhitespace ()
2491 if (currentState == XmlNodeType.None)
2492 currentState = XmlNodeType.XmlDeclaration;
2494 ClearValueBuffer ();
2495 int ch = PeekChar ();
2497 AppendValueChar (ReadChar ());
2498 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2500 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2503 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2504 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2505 SetProperties (nodeType,
2508 null, // value: create only when required
2515 private byte GetBase64Byte (char ch)
2525 if (ch >= 'A' && ch <= 'Z')
2526 return (byte) (ch - 'A');
2527 else if (ch >= 'a' && ch <= 'z')
2528 return (byte) (ch - 'a' + 26);
2529 else if (ch >= '0' && ch <= '9')
2530 return (byte) (ch - '0' + 52);
2532 throw new XmlException ("Invalid Base64 character was found.");
2536 // Returns -1 if it should throw an error.
2537 private int ReadCharsInternal (char [] buffer, int offset, int length)
2539 if (IsEmptyElement) {
2544 shouldSkipUntilEndTag = true;
2547 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2548 else if (length < 0)
2549 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2550 else if (buffer.Length < offset + length)
2551 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2553 if (NodeType != XmlNodeType.Element)
2556 int bufIndex = offset;
2557 for (int i = 0; i < length; i++) {
2558 int c = PeekChar ();
2561 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2564 if (PeekChar () != '/') {
2565 buffer [bufIndex++] = '<';
2568 // Seems to skip immediate EndElement
2575 shouldSkipUntilEndTag = false;
2580 if (c < Char.MaxValue)
2581 buffer [bufIndex++] = (char) c;
2583 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2584 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2592 private bool ReadUntilEndTag ()
2599 throw new XmlException (this as IXmlLineInfo,
2600 "Unexpected end of xml.");
2602 if (PeekChar () != '/')
2605 string name = ReadName ();
2606 if (name != elementNames [elementNameStackPos - 1])
2610 elementNames [--elementNameStackPos] = null;