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 "Multiple document element was detected.");
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.");
1293 string baseUri = GetAttribute ("xml:base");
1294 if (baseUri != null) {
1295 if (this.resolver != null)
1296 parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1298 parserContext.BaseURI = baseUri;
1300 string xmlLang = GetAttribute ("xml:lang");
1301 if (xmlLang != null)
1302 parserContext.XmlLang = xmlLang;
1303 string xmlSpaceAttr = GetAttribute ("xml:space");
1304 if (xmlSpaceAttr != null) {
1305 if (xmlSpaceAttr == "preserve")
1306 parserContext.XmlSpace = XmlSpace.Preserve;
1307 else if (xmlSpaceAttr == "default")
1308 parserContext.XmlSpace = XmlSpace.Default;
1310 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1312 if (PeekChar () == '/') {
1314 isEmptyElement = true;
1319 PushElementName (name);
1320 parserContext.PushScope ();
1325 XmlNodeType.Element, // nodeType
1327 isEmptyElement, // isEmptyElement
1329 false // clearAttributes
1332 if (LookupNamespace (Prefix) == null)
1333 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1335 for (int i = 0; i < attributeCount; i++) {
1336 MoveToAttribute (i);
1337 if (LookupNamespace (Prefix) == null)
1338 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1345 CheckCurrentStateUpdate ();
1348 private void PushElementName (string name)
1350 if (elementNames.Length == elementNameStackPos) {
1351 string [] newArray = new string [elementNames.Length * 2];
1352 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1353 elementNames = newArray;
1355 elementNames [elementNameStackPos++] = name;
1358 // The reader is positioned on the first character
1359 // of the element's name.
1360 private void ReadEndTag ()
1362 if (currentState != XmlNodeType.Element)
1363 throw new XmlException (this as IXmlLineInfo,
1364 "End tag cannot appear in this state.");
1366 string name = ReadName ();
1367 if (elementNameStackPos == 0)
1368 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1369 string expected = elementNames [--elementNameStackPos];
1370 if (expected != name)
1371 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1372 parserContext.PopScope ();
1374 ExpectAfterWhitespace ('>');
1379 XmlNodeType.EndElement, // nodeType
1381 false, // isEmptyElement
1383 true // clearAttributes
1388 CheckCurrentStateUpdate ();
1391 private void CheckCurrentStateUpdate ()
1393 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1394 currentState = XmlNodeType.EndElement;
1397 private void AppendNameChar (int ch)
1399 if (nameLength == nameCapacity)
1400 ExpandNameCapacity ();
1401 if (ch < Char.MaxValue)
1402 nameBuffer [nameLength++] = (char) ch;
1404 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1405 if (nameLength == nameCapacity)
1406 ExpandNameCapacity ();
1407 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1411 private void ExpandNameCapacity ()
1413 nameCapacity = nameCapacity * 2;
1414 char [] oldNameBuffer = nameBuffer;
1415 nameBuffer = new char [nameCapacity];
1416 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1419 private string CreateNameString ()
1421 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1424 private void AppendValueChar (int ch)
1426 if (valueLength == valueCapacity)
1427 ExpandValueCapacity ();
1428 if (ch < Char.MaxValue)
1429 valueBuffer [valueLength++] = (char) ch;
1431 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1432 if (valueLength == valueCapacity)
1433 ExpandValueCapacity ();
1434 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1438 private void ExpandValueCapacity ()
1440 valueCapacity = valueCapacity * 2;
1441 char [] oldValueBuffer = valueBuffer;
1442 valueBuffer = new char [valueCapacity];
1443 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1446 private string CreateValueString ()
1448 return new string (valueBuffer, 0, valueLength);
1451 private void ClearValueBuffer ()
1456 private void AppendCurrentTagChar (int ch)
1458 if (currentTagLength == currentTagCapacity)
1459 ExpandCurrentTagCapacity ();
1460 if (ch < Char.MaxValue)
1461 currentTagBuffer [currentTagLength++] = (char) ch;
1463 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1464 if (currentTagLength == currentTagCapacity)
1465 ExpandCurrentTagCapacity ();
1466 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1470 private void ExpandCurrentTagCapacity ()
1472 currentTagCapacity = currentTagCapacity * 2;
1473 char [] oldCurrentTagBuffer = currentTagBuffer;
1474 currentTagBuffer = new char [currentTagCapacity];
1475 Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1478 private string CreateCurrentTagString ()
1480 return new string (currentTagBuffer, 0, currentTagLength);
1483 private void ClearCurrentTagBuffer ()
1485 currentTagLength = 0;
1488 // The reader is positioned on the first character
1490 private void ReadText (bool notWhitespace)
1492 if (currentState != XmlNodeType.Element)
1493 throw new XmlException (this as IXmlLineInfo,
1494 "Text node cannot appear in this state.");
1497 ClearValueBuffer ();
1499 int ch = PeekChar ();
1500 bool previousWasCloseBracket = false;
1502 while (ch != '<' && ch != -1) {
1505 ch = ReadReference (false);
1506 if (returnEntityReference) // Returns -1 if char validation should not be done
1510 if (XmlChar.IsInvalid (ch))
1511 throw new XmlException (this, "Not allowed character was found.");
1515 AppendValueChar (ch);
1519 if (previousWasCloseBracket)
1520 if (PeekChar () == '>')
1521 throw new XmlException (this as IXmlLineInfo,
1522 "Inside text content, character sequence ']]>' is not allowed.");
1523 previousWasCloseBracket = true;
1525 else if (previousWasCloseBracket)
1526 previousWasCloseBracket = false;
1528 notWhitespace = true;
1531 if (returnEntityReference && valueLength == 0) {
1532 SetEntityReferenceProperties ();
1534 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1535 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1537 nodeType, // nodeType
1538 String.Empty, // name
1539 false, // isEmptyElement
1540 null, // value: create only when required
1541 true // clearAttributes
1546 // The leading '&' has already been consumed.
1547 // Returns true if the entity reference isn't a simple
1548 // character reference or one of the predefined entities.
1549 // This allows the ReadText method to break so that the
1550 // next call to Read will return the EntityReference node.
1551 private int ReadReference (bool ignoreEntityReferences)
1553 if (PeekChar () == '#') {
1555 return ReadCharacterReference ();
1557 return ReadEntityReference (ignoreEntityReferences);
1560 private int ReadCharacterReference ()
1564 if (PeekChar () == 'x') {
1567 while (PeekChar () != ';' && PeekChar () != -1) {
1568 int ch = ReadChar ();
1570 if (ch >= '0' && ch <= '9')
1571 value = (value << 4) + ch - '0';
1572 else if (ch >= 'A' && ch <= 'F')
1573 value = (value << 4) + ch - 'A' + 10;
1574 else if (ch >= 'a' && ch <= 'f')
1575 value = (value << 4) + ch - 'a' + 10;
1577 throw new XmlException (this as IXmlLineInfo,
1579 "invalid hexadecimal digit: {0} (#x{1:X})",
1584 while (PeekChar () != ';' && PeekChar () != -1) {
1585 int ch = ReadChar ();
1587 if (ch >= '0' && ch <= '9')
1588 value = value * 10 + ch - '0';
1590 throw new XmlException (this as IXmlLineInfo,
1592 "invalid decimal digit: {0} (#x{1:X})",
1600 // There is no way to save surrogate pairs...
1601 if (normalization && XmlChar.IsInvalid (value))
1602 throw new XmlException (this as IXmlLineInfo,
1603 "Referenced character was not allowed in XML.");
1607 // Returns -1 if it should not be validated.
1608 // Real EOF must not be detected here.
1609 private int ReadEntityReference (bool ignoreEntityReferences)
1611 string name = ReadName ();
1614 int predefined = XmlChar.GetPredefinedEntity (name);
1615 if (predefined >= 0)
1618 if (ignoreEntityReferences) {
1619 AppendValueChar ('&');
1620 for (int i = 0; i < name.Length; i++)
1621 AppendValueChar (name [i]);
1622 AppendValueChar (';');
1624 returnEntityReference = true;
1625 entityReferenceName = name;
1631 // The reader is positioned on the first character of
1632 // the attribute name.
1633 private void ReadAttributes (bool isXmlDecl)
1636 bool requireWhitespace = false;
1637 currentAttribute = -1;
1638 currentAttributeValue = -1;
1641 if (!SkipWhitespace () && requireWhitespace)
1642 throw new XmlException ("Unexpected token. Name is required here.");
1644 IncrementAttributeToken ();
1645 currentAttributeToken.LineNumber = line;
1646 currentAttributeToken.LinePosition = column;
1648 currentAttributeToken.LocalName =
1649 currentAttributeToken.Name = ReadName ();
1650 ExpectAfterWhitespace ('=');
1652 ReadAttributeValueTokens (-1);
1655 if (currentAttributeToken.Name == "xmlns")
1656 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1657 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1658 string nsPrefix = currentAttributeToken.Name.Substring (6);
1659 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1662 if (!SkipWhitespace ())
1663 requireWhitespace = true;
1664 peekChar = PeekChar ();
1666 if (peekChar == '?')
1669 else if (peekChar == '/' || peekChar == '>')
1671 } while (peekChar != -1);
1673 currentAttribute = -1;
1674 currentAttributeValue = -1;
1677 private void AddAttribute (string name, string value)
1679 IncrementAttributeToken ();
1680 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1681 ati.Name = "SYSTEM";
1683 IncrementAttributeValueToken ();
1684 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1686 SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1690 private void IncrementAttributeToken ()
1693 if (attributeTokens.Length == currentAttribute) {
1694 XmlAttributeTokenInfo [] newArray =
1695 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1696 attributeTokens.CopyTo (newArray, 0);
1697 attributeTokens = newArray;
1699 if (attributeTokens [currentAttribute] == null)
1700 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1701 currentAttributeToken = attributeTokens [currentAttribute];
1702 currentAttributeToken.Clear ();
1705 private void IncrementAttributeValueToken ()
1707 ClearValueBuffer ();
1708 currentAttributeValue++;
1709 if (attributeValueTokens.Length == currentAttributeValue) {
1710 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1711 attributeValueTokens.CopyTo (newArray, 0);
1712 attributeValueTokens = newArray;
1714 if (attributeValueTokens [currentAttributeValue] == null)
1715 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1716 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1717 currentAttributeValueToken.Clear ();
1720 // LAMESPEC: Orthodox XML reader should normalize attribute values
1721 private void ReadAttributeValueTokens (int dummyQuoteChar)
1723 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1725 if (quoteChar != '\'' && quoteChar != '\"')
1726 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1727 currentAttributeToken.QuoteChar = (char) quoteChar;
1729 IncrementAttributeValueToken ();
1730 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1731 currentAttributeValueToken.LineNumber = line;
1732 currentAttributeValueToken.LinePosition = column;
1734 bool incrementToken = false;
1735 bool isNewToken = true;
1740 if (ch == quoteChar)
1743 if (incrementToken) {
1744 IncrementAttributeValueToken ();
1745 currentAttributeValueToken.LineNumber = line;
1746 currentAttributeValueToken.LinePosition = column;
1747 incrementToken = false;
1754 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1756 if (dummyQuoteChar < 0)
1757 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1758 else // Attribute value constructor.
1762 int startPosition = currentTagLength - 1;
1763 if (PeekChar () == '#') {
1765 ch = ReadCharacterReference ();
1766 if (normalization && XmlChar.IsInvalid (ch))
1767 throw new XmlException (this as IXmlLineInfo,
1768 "Not allowed character was found.");
1769 AppendValueChar (ch);
1772 // Check XML 1.0 section 3.1 WFC.
1773 string entName = ReadName ();
1775 int predefined = XmlChar.GetPredefinedEntity (entName);
1776 if (predefined < 0) {
1777 CheckAttributeEntityReferenceWFC (entName);
1778 currentAttributeValueToken.Value = CreateValueString ();
1779 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1781 IncrementAttributeValueToken ();
1782 currentAttributeValueToken.Name = entName;
1783 currentAttributeValueToken.Value = String.Empty;
1784 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1785 incrementToken = true;
1788 AppendValueChar (predefined);
1791 if (normalization && XmlChar.IsInvalid (ch))
1792 throw new XmlException (this, "Invalid character was found.");
1793 AppendValueChar (ch);
1799 if (!incrementToken) {
1800 currentAttributeValueToken.Value = CreateValueString ();
1801 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1803 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1807 private void CheckAttributeEntityReferenceWFC (string entName)
1809 DTDEntityDeclaration entDecl =
1810 DTD == null ? null : DTD.EntityDecls [entName];
1811 if (DTD != null && resolver != null && entDecl == null)
1812 throw new XmlException (this, "Referenced entity does not exist.");
1814 if (entDecl == null)
1817 if (entDecl.HasExternalReference)
1818 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
1819 if (isStandalone && !entDecl.IsInternalSubset)
1820 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
1821 if (entDecl.EntityValue.IndexOf ('<') >= 0)
1822 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1825 // The reader is positioned on the first character
1828 // It may be xml declaration or processing instruction.
1829 private void ReadProcessingInstruction ()
1831 string target = ReadName ();
1832 if (target == "xml") {
1833 ReadXmlDeclaration ();
1835 } else if (target.ToLower () == "xml")
1836 throw new XmlException (this as IXmlLineInfo,
1837 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1839 if (currentState == XmlNodeType.None)
1840 currentState = XmlNodeType.XmlDeclaration;
1842 if (!SkipWhitespace ())
1843 if (PeekChar () != '?')
1844 throw new XmlException (this as IXmlLineInfo,
1845 "Invalid processing instruction name was found.");
1847 ClearValueBuffer ();
1849 while (PeekChar () != -1) {
1850 int ch = ReadChar ();
1852 if (ch == '?' && PeekChar () == '>') {
1857 if (normalization && XmlChar.IsInvalid (ch))
1858 throw new XmlException (this, "Invalid character was found.");
1859 AppendValueChar (ch);
1863 XmlNodeType.ProcessingInstruction, // nodeType
1865 false, // isEmptyElement
1866 null, // value: create only when required
1867 true // clearAttributes
1871 // The reader is positioned after "<?xml "
1872 private void ReadXmlDeclaration ()
1874 if (currentState != XmlNodeType.None) {
1875 throw new XmlException (this as IXmlLineInfo,
1876 "XML declaration cannot appear in this state.");
1878 currentState = XmlNodeType.XmlDeclaration;
1882 ReadAttributes (true); // They must have "version."
1883 string version = GetAttribute ("version");
1885 string message = null;
1887 if (attributeTokens [0].Name != "version" || version != "1.0")
1888 message = "Version 1.0 declaration is required in XML Declaration.";
1889 else if (attributeCount > 1 &&
1890 (attributeTokens [1].Name != "encoding" &&
1891 attributeTokens [1].Name != "standalone"))
1892 message = "Invalid Xml Declaration markup was found.";
1893 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1894 message = "Invalid Xml Declaration markup was found.";
1895 string sa = GetAttribute ("standalone");
1896 if (sa != null && sa != "yes" && sa != "no")
1897 message = "Only 'yes' or 'no' is allowed for standalone.";
1899 this.isStandalone = (sa == "yes");
1901 if (message != null)
1902 throw new XmlException (this as IXmlLineInfo, message);
1905 XmlNodeType.XmlDeclaration, // nodeType
1907 false, // isEmptyElement
1908 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1909 false // clearAttributes
1915 internal void SkipTextDeclaration ()
1917 this.currentState = XmlNodeType.Element;
1919 if (PeekChar () != '<')
1924 if (PeekChar () != '?') {
1930 while (peekCharsIndex < 6) {
1931 if (PeekChar () < 0)
1936 if (new string (peekChars, 2, 4) != "xml ") {
1937 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1938 throw new XmlException (this as IXmlLineInfo,
1939 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1948 if (PeekChar () == 'v') {
1950 ExpectAfterWhitespace ('=');
1952 int quoteChar = ReadChar ();
1953 char [] expect1_0 = new char [3];
1954 int versionLength = 0;
1955 switch (quoteChar) {
1958 while (PeekChar () != quoteChar) {
1959 if (PeekChar () == -1)
1960 throw new XmlException (this as IXmlLineInfo,
1961 "Invalid version declaration inside text declaration.");
1962 else if (versionLength == 3)
1963 throw new XmlException (this as IXmlLineInfo,
1964 "Invalid version number inside text declaration.");
1966 expect1_0 [versionLength] = (char) ReadChar ();
1968 if (versionLength == 3 && new String (expect1_0) != "1.0")
1969 throw new XmlException (this as IXmlLineInfo,
1970 "Invalid version number inside text declaration.");
1977 throw new XmlException (this as IXmlLineInfo,
1978 "Invalid version declaration inside text declaration.");
1982 if (PeekChar () == 'e') {
1983 Expect ("encoding");
1984 ExpectAfterWhitespace ('=');
1986 int quoteChar = ReadChar ();
1987 switch (quoteChar) {
1990 while (PeekChar () != quoteChar)
1991 if (ReadChar () == -1)
1992 throw new XmlException (this as IXmlLineInfo,
1993 "Invalid encoding declaration inside text declaration.");
1998 throw new XmlException (this as IXmlLineInfo,
1999 "Invalid encoding declaration inside text declaration.");
2001 // Encoding value should be checked inside XmlInputStream.
2004 throw new XmlException (this as IXmlLineInfo,
2005 "Encoding declaration is mandatory in text declaration.");
2010 // The reader is positioned on the first character after
2011 // the leading '<!'.
2012 private void ReadDeclaration ()
2014 int ch = PeekChar ();
2032 throw new XmlException (this as IXmlLineInfo,
2033 "Unexpected declaration markup was found.");
2037 // The reader is positioned on the first character after
2038 // the leading '<!--'.
2039 private void ReadComment ()
2041 if (currentState == XmlNodeType.None)
2042 currentState = XmlNodeType.XmlDeclaration;
2044 ClearValueBuffer ();
2046 while (PeekChar () != -1) {
2047 int ch = ReadChar ();
2049 if (ch == '-' && PeekChar () == '-') {
2052 if (PeekChar () != '>')
2053 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2059 if (XmlChar.IsInvalid (ch))
2060 throw new XmlException (this as IXmlLineInfo,
2061 "Not allowed character was found.");
2063 AppendValueChar (ch);
2067 XmlNodeType.Comment, // nodeType
2068 String.Empty, // name
2069 false, // isEmptyElement
2070 null, // value: create only when required
2071 true // clearAttributes
2075 // The reader is positioned on the first character after
2076 // the leading '<![CDATA['.
2077 private void ReadCDATA ()
2079 if (currentState != XmlNodeType.Element)
2080 throw new XmlException (this as IXmlLineInfo,
2081 "CDATA section cannot appear in this state.");
2083 ClearValueBuffer ();
2087 while (PeekChar () != -1) {
2092 if (ch == ']' && PeekChar () == ']') {
2093 ch = ReadChar (); // ']'
2095 if (PeekChar () == '>') {
2102 if (normalization && XmlChar.IsInvalid (ch))
2103 throw new XmlException (this, "Invalid character was found.");
2105 AppendValueChar (ch);
2109 XmlNodeType.CDATA, // nodeType
2110 String.Empty, // name
2111 false, // isEmptyElement
2112 null, // value: create only when required
2113 true // clearAttributes
2117 // The reader is positioned on the first character after
2118 // the leading '<!DOCTYPE'.
2119 private void ReadDoctypeDecl ()
2121 switch (currentState) {
2122 case XmlNodeType.DocumentType:
2123 case XmlNodeType.Element:
2124 case XmlNodeType.EndElement:
2125 throw new XmlException (this as IXmlLineInfo,
2126 "Document type cannot appear in this state.");
2128 currentState = XmlNodeType.DocumentType;
2130 string doctypeName = null;
2131 string publicId = null;
2132 string systemId = null;
2133 int intSubsetStartLine = 0;
2134 int intSubsetStartColumn = 0;
2137 doctypeName = ReadName ();
2142 systemId = ReadSystemLiteral (true);
2145 publicId = ReadPubidLiteral ();
2146 if (!SkipWhitespace ())
2147 throw new XmlException (this as IXmlLineInfo,
2148 "Whitespace is required between PUBLIC id and SYSTEM id.");
2149 systemId = ReadSystemLiteral (false);
2155 if(PeekChar () == '[')
2157 // read markupdecl etc. or end of decl
2159 intSubsetStartLine = this.LineNumber;
2160 intSubsetStartColumn = this.LinePosition;
2161 int startPos = currentTagLength;
2162 ReadInternalSubset ();
2163 int endPos = currentTagLength - 1;
2164 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2166 // end of DOCTYPE decl.
2167 ExpectAfterWhitespace ('>');
2169 GenerateDTDObjectModel (doctypeName, publicId,
2170 systemId, parserContext.InternalSubset,
2171 intSubsetStartLine, intSubsetStartColumn);
2173 // set properties for <!DOCTYPE> node
2175 XmlNodeType.DocumentType, // nodeType
2176 doctypeName, // name
2177 false, // isEmptyElement
2178 parserContext.InternalSubset, // value
2179 true // clearAttributes
2182 if (publicId != null)
2183 AddAttribute ("PUBLIC", publicId);
2184 if (systemId != null)
2185 AddAttribute ("SYSTEM", systemId);
2186 currentAttribute = currentAttributeValue = -1;
2189 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2190 string systemId, string internalSubset)
2192 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2195 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2196 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2199 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2200 DTD.BaseURI = BaseURI;
2202 DTD.PublicId = publicId;
2203 DTD.SystemId = systemId;
2204 DTD.InternalSubset = internalSubset;
2205 DTD.XmlResolver = resolver;
2206 DTD.IsStandalone = isStandalone;
2207 DTD.LineNumber = line;
2208 DTD.LinePosition = column;
2210 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2211 dr.Normalization = this.normalization;
2212 #if DTD_HANDLE_EVENTS
2213 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2215 return dr.GenerateDTDObjectModel ();
2218 private void OnValidationEvent (object o, ValidationEventArgs e)
2220 #if DTD_HANDLE_EVENTS
2221 if (ValidationEventHandler != null)
2222 // Override object as this.
2223 ValidationEventHandler (this, e);
2227 private enum DtdInputState
2240 private class DtdInputStateStack
2242 Stack intern = new Stack ();
2243 public DtdInputStateStack ()
2245 Push (DtdInputState.Free);
2248 public DtdInputState Peek ()
2250 return (DtdInputState) intern.Peek ();
2253 public DtdInputState Pop ()
2255 return (DtdInputState) intern.Pop ();
2258 public void Push (DtdInputState val)
2265 DtdInputStateStack stateStack = new DtdInputStateStack ();
2266 DtdInputState State {
2267 get { return stateStack.Peek (); }
2270 // Simply read but not generate any result.
2271 private void ReadInternalSubset ()
2273 bool continueParse = true;
2275 while (continueParse) {
2276 switch (ReadChar ()) {
2279 case DtdInputState.Free:
2280 continueParse = false;
2282 case DtdInputState.InsideDoubleQuoted:
2284 case DtdInputState.InsideSingleQuoted:
2287 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2291 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2293 if (State == DtdInputState.InsideDoubleQuoted ||
2294 State == DtdInputState.InsideSingleQuoted)
2295 continue; // well-formed
2296 switch (ReadChar ()) {
2298 stateStack.Push (DtdInputState.PI);
2301 switch (ReadChar ()) {
2303 switch (ReadChar ()) {
2306 stateStack.Push (DtdInputState.ElementDecl);
2310 stateStack.Push (DtdInputState.EntityDecl);
2313 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2318 stateStack.Push (DtdInputState.AttlistDecl);
2322 stateStack.Push (DtdInputState.NotationDecl);
2326 stateStack.Push (DtdInputState.Comment);
2331 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2335 if (State == DtdInputState.InsideSingleQuoted)
2337 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2338 stateStack.Push (DtdInputState.InsideSingleQuoted);
2341 if (State == DtdInputState.InsideDoubleQuoted)
2343 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2344 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2348 case DtdInputState.ElementDecl:
2349 goto case DtdInputState.NotationDecl;
2350 case DtdInputState.AttlistDecl:
2351 goto case DtdInputState.NotationDecl;
2352 case DtdInputState.EntityDecl:
2353 goto case DtdInputState.NotationDecl;
2354 case DtdInputState.NotationDecl:
2357 case DtdInputState.InsideDoubleQuoted:
2359 case DtdInputState.InsideSingleQuoted:
2360 continue; // well-formed
2361 case DtdInputState.Comment:
2364 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2368 if (State == DtdInputState.PI) {
2369 if (ReadChar () == '>')
2374 if (State == DtdInputState.Comment) {
2375 if (PeekChar () == '-') {
2383 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2384 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2390 // The reader is positioned on the first 'S' of "SYSTEM".
2391 private string ReadSystemLiteral (bool expectSYSTEM)
2395 if (!SkipWhitespace ())
2396 throw new XmlException (this as IXmlLineInfo,
2397 "Whitespace is required after 'SYSTEM'.");
2401 int quoteChar = ReadChar (); // apos or quot
2402 int startPos = currentTagLength;
2404 ClearValueBuffer ();
2405 while (c != quoteChar) {
2408 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2410 AppendValueChar (c);
2412 return CreateValueString ();
2415 private string ReadPubidLiteral()
2418 if (!SkipWhitespace ())
2419 throw new XmlException (this as IXmlLineInfo,
2420 "Whitespace is required after 'PUBLIC'.");
2421 int quoteChar = ReadChar ();
2422 int startPos = currentTagLength;
2424 ClearValueBuffer ();
2425 while(c != quoteChar)
2428 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2429 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2430 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2432 AppendValueChar (c);
2434 return CreateValueString ();
2437 // The reader is positioned on the first character
2439 private string ReadName ()
2441 int ch = PeekChar ();
2442 if (!XmlChar.IsFirstNameChar (ch))
2443 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2447 AppendNameChar (ReadChar ());
2449 while (XmlChar.IsNameChar (PeekChar ())) {
2450 AppendNameChar (ReadChar ());
2453 return CreateNameString ();
2456 // Read the next character and compare it against the
2457 // specified character.
2458 private void Expect (int expected)
2460 int ch = ReadChar ();
2462 if (ch != expected) {
2463 throw new XmlException (this as IXmlLineInfo,
2465 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2473 private void Expect (string expected)
2475 int len = expected.Length;
2476 for(int i=0; i< len; i++)
2477 Expect (expected[i]);
2480 private void ExpectAfterWhitespace (char c)
2483 int i = ReadChar ();
2484 if (i < 0x21 && XmlChar.IsWhitespace (i))
2487 throw new XmlException (this, String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2492 // Does not consume the first non-whitespace character.
2493 private bool SkipWhitespace ()
2495 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2498 while (XmlChar.IsWhitespace (PeekChar ()))
2503 private void ReadWhitespace ()
2505 if (currentState == XmlNodeType.None)
2506 currentState = XmlNodeType.XmlDeclaration;
2508 ClearValueBuffer ();
2509 int ch = PeekChar ();
2511 AppendValueChar (ReadChar ());
2512 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2514 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2517 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2518 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2519 SetProperties (nodeType,
2522 null, // value: create only when required
2529 private byte GetBase64Byte (char ch)
2539 if (ch >= 'A' && ch <= 'Z')
2540 return (byte) (ch - 'A');
2541 else if (ch >= 'a' && ch <= 'z')
2542 return (byte) (ch - 'a' + 26);
2543 else if (ch >= '0' && ch <= '9')
2544 return (byte) (ch - '0' + 52);
2546 throw new XmlException ("Invalid Base64 character was found.");
2550 // Returns -1 if it should throw an error.
2551 private int ReadCharsInternal (char [] buffer, int offset, int length)
2553 if (IsEmptyElement) {
2558 shouldSkipUntilEndTag = true;
2561 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2562 else if (length < 0)
2563 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2564 else if (buffer.Length < offset + length)
2565 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2567 if (NodeType != XmlNodeType.Element)
2570 int bufIndex = offset;
2571 for (int i = 0; i < length; i++) {
2572 int c = PeekChar ();
2575 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2578 if (PeekChar () != '/') {
2579 buffer [bufIndex++] = '<';
2582 // Seems to skip immediate EndElement
2589 shouldSkipUntilEndTag = false;
2594 if (c < Char.MaxValue)
2595 buffer [bufIndex++] = (char) c;
2597 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2598 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2606 private bool ReadUntilEndTag ()
2613 throw new XmlException (this as IXmlLineInfo,
2614 "Unexpected end of xml.");
2616 if (PeekChar () != '/')
2619 string name = ReadName ();
2620 if (name != elementNames [elementNameStackPos - 1])
2624 elementNames [--elementNameStackPos] = null;