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 // NameTables aren't being used completely yet.
16 // Some thought needs to be given to performance. There's too many
17 // strings being allocated.
19 // If current node is on an Attribute, Prefix might be null, and
20 // in several fields which uses XmlReader, it should be considered.
24 using System.Collections;
25 using System.Collections.Specialized;
27 using System.Security.Policy;
29 using System.Xml.Schema;
31 using Mono.Xml.Native;
35 public class XmlTextReader : XmlReader, IXmlLineInfo
39 protected XmlTextReader ()
43 public XmlTextReader (Stream input)
44 : this (new XmlStreamReader (input))
48 public XmlTextReader (string url)
49 : this(url, new NameTable ())
53 public XmlTextReader (TextReader input)
54 : this (input, new NameTable ())
58 protected XmlTextReader (XmlNameTable nt)
59 : this (String.Empty, null, XmlNodeType.None, null)
63 public XmlTextReader (Stream input, XmlNameTable nt)
64 : this(new XmlStreamReader (input), nt)
68 public XmlTextReader (string url, Stream input)
69 : this (url, new XmlStreamReader (input))
73 public XmlTextReader (string url, TextReader input)
74 : this (url, input, new NameTable ())
78 public XmlTextReader (string url, XmlNameTable nt)
80 XmlUrlResolver xr = new XmlUrlResolver ();
81 Uri uri = xr.ResolveUri (null, url);
82 Stream s = xr.GetEntity (uri, null, typeof (Stream)) as Stream;
83 XmlParserContext ctx = new XmlParserContext (nt,
84 new XmlNamespaceManager (nt),
87 this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document);
90 public XmlTextReader (TextReader input, XmlNameTable nt)
91 : this (String.Empty, input, nt)
95 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
96 : this (context != null ? context.BaseURI : String.Empty,
97 new XmlStreamReader (xmlFragment),
103 public XmlTextReader (string url, Stream input, XmlNameTable nt)
104 : this (url, new XmlStreamReader (input), nt)
108 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
109 : this (url, input, XmlNodeType.Document, null)
113 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
114 : this (context != null ? context.BaseURI : String.Empty,
115 new StringReader (xmlFragment),
121 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
123 InitializeContext (url, context, fragment, fragType);
130 public override int AttributeCount
132 get { return attributeCount; }
135 public override string BaseURI
137 get { return parserContext.BaseURI; }
140 public override int Depth
143 if (currentAttributeValue >= 0)
144 return elementDepth + 2; // inside attribute value.
145 else if (currentAttribute >= 0)
146 return elementDepth + 1;
151 public Encoding Encoding
153 get { return parserContext.Encoding; }
157 public EntityHandling EntityHandling {
158 get { throw new NotImplementedException (); }
162 public override bool EOF
167 readState == ReadState.EndOfFile ||
168 readState == ReadState.Closed;
174 public override Evidence [] Evidences {
175 get { return base.Evidences; }
179 public override bool HasValue
182 if (this.valueBuilderAvailable)
183 return valueBuilder.Length != 0;
185 return cursorToken.Value != null;
189 public override bool IsDefault
193 // XmlTextReader does not expand default attributes.
198 public override bool IsEmptyElement
200 get { return cursorToken.IsEmptyElement; }
203 public override string this [int i]
205 get { return GetAttribute (i); }
208 public override string this [string name]
210 get { return GetAttribute (name); }
213 public override string this [string localName, string namespaceName]
215 get { return GetAttribute (localName, namespaceName); }
218 public int LineNumber
221 if (useProceedingLineInfo)
224 return cursorToken.LineNumber;
228 public int LinePosition
231 if (useProceedingLineInfo)
234 return cursorToken.LinePosition;
238 public override string LocalName
240 get { return cursorToken.LocalName; }
243 public override string Name
245 get { return cursorToken.Name; }
248 public bool Namespaces
250 get { return namespaces; }
252 if (readState != ReadState.Initial)
253 throw new InvalidOperationException ("Namespaces have to be set before reading.");
258 public override string NamespaceURI
260 get { return cursorToken.NamespaceURI; }
263 public override XmlNameTable NameTable
265 get { return parserContext.NameTable; }
268 public override XmlNodeType NodeType
270 get { return cursorToken.NodeType; }
273 public bool Normalization
275 get { return normalization; }
276 set { normalization = value; }
279 public override string Prefix
281 get { return cursorToken.Prefix; }
284 public override char QuoteChar
286 get { return cursorToken.QuoteChar; }
289 public override ReadState ReadState
291 get { return readState; }
294 public override string Value
296 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
299 public WhitespaceHandling WhitespaceHandling
301 get { return whitespaceHandling; }
302 set { whitespaceHandling = value; }
305 public override string XmlLang
307 get { return parserContext.XmlLang; }
310 public XmlResolver XmlResolver
312 set { resolver = value; }
315 public override XmlSpace XmlSpace
317 get { return parserContext.XmlSpace; }
324 public override void Close ()
326 readState = ReadState.Closed;
328 cursorToken.Clear ();
329 currentToken.Clear ();
335 public override string GetAttribute (int i)
337 if (i > attributeCount)
338 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
340 return attributeTokens [i].Value;
344 // MS.NET 1.0 msdn says that this method returns String.Empty
345 // for absent attribute, but in fact it returns null.
346 // This description is corrected in MS.NET 1.1 msdn.
347 public override string GetAttribute (string name)
349 for (int i = 0; i < attributeCount; i++)
350 if (attributeTokens [i].Name == name)
351 return attributeTokens [i].Value;
355 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
357 for (int i = 0; i < attributeCount; i++) {
358 XmlAttributeTokenInfo ti = attributeTokens [i];
359 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
365 internal XmlParserContext GetInternalParserContext ()
367 return parserContext;
370 public override string GetAttribute (string localName, string namespaceURI)
372 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
375 return attributeTokens [idx].Value;
378 public TextReader GetRemainder ()
380 StringBuilder sb = null;
381 if (this.hasPeekChars) {
382 sb = new StringBuilder ();
384 for (; end < 6; end++)
385 if (peekChars [end] <= 0)
387 sb.Append (peekChars, 0, end);
389 if (has_peek && peek_char > 0) {
391 sb.Append (ExpandSurrogateChar (peek_char));
393 return new StringReader (ExpandSurrogateChar (peek_char) + reader.ReadToEnd ());
395 // As long as less memory consumptive...
397 return new StringReader (sb.ToString () + reader.ReadToEnd ());
399 return new StringReader (reader.ReadToEnd ());
402 bool IXmlLineInfo.HasLineInfo ()
407 public override string LookupNamespace (string prefix)
409 return LookupNamespace (prefix, false);
412 internal string LookupNamespace (string prefix, bool atomizedName)
414 return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
417 public override void MoveToAttribute (int i)
419 if (i >= attributeCount)
420 throw new ArgumentOutOfRangeException ("attribute index out of range.");
422 currentAttribute = i;
423 currentAttributeValue = -1;
424 cursorToken = attributeTokens [i];
427 public override bool MoveToAttribute (string name)
429 for (int i = 0; i < attributeCount; i++) {
430 XmlAttributeTokenInfo ti = attributeTokens [i];
431 if (ti.Name == name) {
439 public override bool MoveToAttribute (string localName, string namespaceName)
441 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
444 MoveToAttribute (idx);
448 public override bool MoveToElement ()
450 if (currentToken == null) // for attribute .ctor()
453 if (cursorToken == currentToken)
456 if (currentAttribute >= 0) {
457 currentAttribute = -1;
458 currentAttributeValue = -1;
459 cursorToken = currentToken;
466 public override bool MoveToFirstAttribute ()
468 if (attributeCount == 0)
471 return MoveToNextAttribute ();
474 public override bool MoveToNextAttribute ()
476 if (currentAttribute == 0 && attributeCount == 0)
478 if (currentAttribute + 1 < attributeCount) {
480 currentAttributeValue = -1;
481 cursorToken = attributeTokens [currentAttribute];
488 public override bool Read ()
490 if (startNodeType == XmlNodeType.Attribute) {
491 if (currentAttribute == 0)
492 return false; // already read.
494 IncrementAttributeToken ();
495 ReadAttributeValueTokens ('"');
496 cursorToken = attributeTokens [0];
497 currentAttributeValue = -1;
498 readState = ReadState.Interactive;
503 readState = ReadState.Interactive;
504 currentLinkedNodeLineNumber = line;
505 currentLinkedNodeLinePosition = column;
506 useProceedingLineInfo = true;
508 cursorToken = currentToken;
510 currentAttribute = currentAttributeValue = -1;
511 currentToken.Clear ();
513 // It was moved from end of ReadStartTag ().
518 if (shouldSkipUntilEndTag) {
519 shouldSkipUntilEndTag = false;
520 return ReadUntilEndTag ();
523 base64CacheStartsAt = -1;
525 more = ReadContent ();
527 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
528 currentState = XmlNodeType.EndElement;
530 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
531 throw new XmlException ("Document element did not appear.");
533 useProceedingLineInfo = false;
537 public override bool ReadAttributeValue ()
539 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
543 if (currentAttribute < 0)
545 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
546 if (currentAttributeValue < 0)
547 currentAttributeValue = ti.ValueTokenStartIndex - 1;
549 if (currentAttributeValue < ti.ValueTokenEndIndex) {
550 currentAttributeValue++;
551 cursorToken = attributeValueTokens [currentAttributeValue];
558 public int ReadBase64 (byte [] buffer, int offset, int length)
561 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
563 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
564 else if (buffer.Length < offset + length)
565 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
567 if (length == 0) // It does not raise an error.
570 int bufIndex = offset;
571 int bufLast = offset + length;
573 if (base64CacheStartsAt >= 0) {
574 for (int i = base64CacheStartsAt; i < 3; i++) {
575 buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
576 if (bufIndex == bufLast)
577 return bufLast - offset;
581 for (int i = 0; i < 3; i++)
583 base64CacheStartsAt = -1;
585 int max = (int) System.Math.Ceiling (4.0 / 3 * length);
586 int additional = max % 4;
588 max += 4 - additional;
589 char [] chars = new char [max];
590 int charsLength = ReadChars (chars, 0, max);
594 for (int i = 0; i < charsLength - 3; i += 4) {
595 b = (byte) (GetBase64Byte (chars [i]) << 2);
596 if (bufIndex < bufLast)
597 buffer [bufIndex] = b;
599 if (base64CacheStartsAt < 0)
600 base64CacheStartsAt = 0;
603 // charsLength mod 4 might not equals to 0.
604 if (i + 1 == charsLength)
606 b = GetBase64Byte (chars [i + 1]);
607 work = (byte) (b >> 4);
608 if (bufIndex < bufLast) {
609 buffer [bufIndex] += work;
613 base64Cache [0] += work;
615 work = (byte) ((b & 0xf) << 4);
616 if (bufIndex < bufLast) {
617 buffer [bufIndex] = work;
620 if (base64CacheStartsAt < 0)
621 base64CacheStartsAt = 1;
622 base64Cache [1] = work;
625 if (i + 2 == charsLength)
627 b = GetBase64Byte (chars [i + 2]);
628 work = (byte) (b >> 2);
629 if (bufIndex < bufLast) {
630 buffer [bufIndex] += work;
634 base64Cache [1] += work;
636 work = (byte) ((b & 3) << 6);
637 if (bufIndex < bufLast)
638 buffer [bufIndex] = work;
640 if (base64CacheStartsAt < 0)
641 base64CacheStartsAt = 2;
642 base64Cache [2] = work;
644 if (i + 3 == charsLength)
646 work = GetBase64Byte (chars [i + 3]);
647 if (bufIndex < bufLast) {
648 buffer [bufIndex] += work;
652 base64Cache [2] += work;
654 return System.Math.Min (bufLast - offset, bufIndex - offset);
657 public int ReadBinHex (byte [] buffer, int offset, int length)
660 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
662 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
663 else if (buffer.Length < offset + length)
664 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
669 char [] chars = new char [length * 2];
670 int charsLength = ReadChars (chars, 0, length * 2);
671 return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
674 public int ReadChars (char [] buffer, int offset, int length)
676 return ReadCharsInternal (buffer, offset, length);
680 StringBuilder innerXmlBuilder;
681 public override string ReadInnerXml ()
683 if (readState != ReadState.Interactive)
687 case XmlNodeType.Attribute:
689 case XmlNodeType.Element:
693 int startDepth = depth;
695 if (innerXmlBuilder == null)
696 innerXmlBuilder = new StringBuilder ();
697 innerXmlBuilder.Length = 0;
701 if (NodeType ==XmlNodeType.None)
702 throw new XmlException ("unexpected end of xml.");
703 else if (NodeType == XmlNodeType.EndElement && depth == startDepth) {
708 innerXmlBuilder.Append (currentTag);
710 string xml = innerXmlBuilder.ToString ();
711 innerXmlBuilder.Length = 0;
713 case XmlNodeType.None:
714 // MS document is incorrect. Seems not to progress.
722 public override string ReadOuterXml ()
724 if (readState != ReadState.Interactive)
728 case XmlNodeType.Attribute:
729 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
730 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
731 case XmlNodeType.Element:
732 bool isEmpty = IsEmptyElement;
733 string startTag = currentTag.ToString ();
736 if (NodeType == XmlNodeType.Element && !isEmpty)
737 return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
739 return currentTag.ToString ();
740 case XmlNodeType.None:
741 // MS document is incorrect. Seems not to progress.
750 public override string ReadString ()
752 return ReadStringInternal ();
755 public void ResetState ()
760 public override void ResolveEntity ()
762 // XmlTextReader does not resolve entities.
763 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
767 [MonoTODO ("Implement for performance reason")]
768 public override void Skip ()
776 // Parsed DTD Objects
777 #if DTD_HANDLE_EVENTS
778 internal event ValidationEventHandler ValidationEventHandler;
781 internal DTDObjectModel DTD {
782 get { return parserContext.Dtd; }
785 internal XmlResolver Resolver {
786 get { return resolver; }
791 internal class XmlTokenInfo
793 public XmlTokenInfo (XmlTextReader xtr)
801 protected XmlTextReader Reader;
804 public string LocalName;
805 public string Prefix;
806 public string NamespaceURI;
807 public bool IsEmptyElement;
808 public char QuoteChar;
809 public int LineNumber;
810 public int LinePosition;
812 public XmlNodeType NodeType;
814 public virtual string Value {
816 if (valueCache != null)
818 else if (Reader.valueBuilderAvailable) {
819 valueCache = Reader.valueBuilder.ToString ();
829 public virtual void Clear ()
832 NodeType = XmlNodeType.None;
833 Name = LocalName = Prefix = NamespaceURI = String.Empty;
834 IsEmptyElement = false;
836 LineNumber = LinePosition = 0;
839 internal virtual void FillNames ()
841 if (Reader.Namespaces) {
842 int indexOfColon = Name.IndexOf (':');
844 if (indexOfColon == -1) {
845 Prefix = String.Empty;
848 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
849 // However, this might be reverted if NameTable is got improved.
850 char [] nameArr = Name.ToCharArray ();
851 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
852 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
853 // Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
854 // LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
859 case XmlNodeType.Attribute:
860 if (Prefix == string.Empty)
861 NamespaceURI = string.Empty;
863 NamespaceURI = Reader.LookupNamespace (Prefix, true);
866 case XmlNodeType.Element:
867 case XmlNodeType.EndElement:
868 NamespaceURI = Reader.LookupNamespace (Prefix, true);
875 Prefix = String.Empty;
881 internal class XmlAttributeTokenInfo : XmlTokenInfo
883 public XmlAttributeTokenInfo (XmlTextReader reader)
886 NodeType = XmlNodeType.Attribute;
889 public int ValueTokenStartIndex;
890 public int ValueTokenEndIndex;
892 bool cachedNormalization;
893 StringBuilder tmpBuilder = new StringBuilder ();
895 public override string Value {
897 if (cachedNormalization != Reader.Normalization)
899 if (valueCache != null)
901 cachedNormalization = Reader.Normalization;
903 // An empty value should return String.Empty.
904 if (ValueTokenStartIndex == ValueTokenEndIndex) {
905 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
906 if (ti.NodeType == XmlNodeType.EntityReference)
907 valueCache = String.Concat ("&", ti.Name, ";");
909 valueCache = ti.Value;
910 if (cachedNormalization)
915 tmpBuilder.Length = 0;
916 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
917 XmlTokenInfo ti = Reader.attributeValueTokens [i];
918 if (ti.NodeType == XmlNodeType.Text)
919 tmpBuilder.Append (ti.Value);
921 tmpBuilder.Append ('&');
922 tmpBuilder.Append (ti.Name);
923 tmpBuilder.Append (';');
927 valueCache = tmpBuilder.ToString ();
928 if (cachedNormalization)
937 public override void Clear ()
941 NodeType = XmlNodeType.Attribute;
942 ValueTokenStartIndex = ValueTokenEndIndex = 0;
945 internal override void FillNames ()
948 if (Prefix == "xmlns" || Name == "xmlns")
949 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
952 private void NormalizeSpaces ()
954 tmpBuilder.Length = 0;
955 for (int i = 0; i < valueCache.Length; i++)
956 switch (valueCache [i]) {
958 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
963 tmpBuilder.Append (' ');
966 tmpBuilder.Append (valueCache [i]);
969 valueCache = tmpBuilder.ToString ();
973 private XmlTokenInfo cursorToken;
974 private XmlTokenInfo currentToken;
975 private XmlAttributeTokenInfo currentAttributeToken;
976 private XmlTokenInfo currentAttributeValueToken;
977 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
978 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
979 private int currentAttribute;
980 private int currentAttributeValue;
981 private int attributeCount;
983 private XmlParserContext parserContext;
985 private ReadState readState;
988 private int elementDepth;
989 private bool depthUp;
991 private bool popScope;
992 private Stack elementStack = new Stack();
993 private bool allowMultipleRoot;
995 private bool isStandalone;
997 private StringBuilder valueBuilder;
998 private bool valueBuilderAvailable = false;
1000 private bool returnEntityReference;
1001 private string entityReferenceName;
1003 private char [] nameBuffer;
1004 private int nameLength;
1005 private int nameCapacity;
1006 private const int initialNameCapacity = 256;
1008 private StringBuilder valueBuffer = new StringBuilder (512);
1010 private TextReader reader;
1011 private bool can_seek;
1012 private bool has_peek;
1013 private int peek_char;
1014 private bool hasPeekChars;
1015 private char [] peekChars;
1016 private int peekCharsIndex;
1020 private StringBuilder currentTag = new StringBuilder ();
1022 private int currentLinkedNodeLineNumber;
1023 private int currentLinkedNodeLinePosition;
1024 private bool useProceedingLineInfo;
1026 // A buffer for ReadContent for ReadOuterXml
1028 private XmlNodeType startNodeType;
1029 // State machine attribute.
1030 // XmlDeclaration: after the first node.
1031 // DocumentType: after doctypedecl
1032 // Element: inside document element
1033 // EndElement: after document element
1034 private XmlNodeType currentState;
1036 // For ReadChars()/ReadBase64()/ReadBinHex()
1037 private bool shouldSkipUntilEndTag;
1038 private byte [] base64Cache = new byte [3];
1039 private int base64CacheStartsAt;
1041 // These values are never re-initialized.
1042 private bool namespaces = true;
1043 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
1044 private XmlResolver resolver = new XmlUrlResolver ();
1045 private bool normalization = false;
1047 private void Init ()
1049 currentToken = new XmlTokenInfo (this);
1050 cursorToken = currentToken;
1051 currentAttribute = -1;
1052 currentAttributeValue = -1;
1055 readState = ReadState.Initial;
1056 allowMultipleRoot = false;
1062 popScope = allowMultipleRoot = false;
1063 elementStack.Clear ();
1065 isStandalone = false;
1066 valueBuilderAvailable = false;
1067 returnEntityReference = false;
1068 entityReferenceName = String.Empty;
1070 nameBuffer = new char [initialNameCapacity];
1072 nameCapacity = initialNameCapacity;
1074 can_seek = has_peek = false;
1075 peek_char = peekCharsIndex = 0;
1076 peekChars = new char [6];
1080 currentTag.Length = 0;
1082 valueBuffer.Length = 0;
1084 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
1085 useProceedingLineInfo = false;
1087 currentState = XmlNodeType.None;
1089 shouldSkipUntilEndTag = false;
1090 base64CacheStartsAt = -1;
1093 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1095 startNodeType = fragType;
1096 parserContext = context;
1097 if (context == null) {
1098 XmlNameTable nt = new NameTable ();
1099 parserContext = new XmlParserContext (nt,
1100 new XmlNamespaceManager (nt),
1105 if (url != null && url.Length > 0) {
1108 uri = new Uri (url);
1109 } catch (Exception) {
1110 string path = Path.GetFullPath ("./a");
1111 uri = new Uri (new Uri (path), url);
1113 parserContext.BaseURI = uri.ToString ();
1119 case XmlNodeType.Attribute:
1120 fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1122 case XmlNodeType.Element:
1123 currentState = XmlNodeType.Element;
1124 allowMultipleRoot = true;
1126 case XmlNodeType.Document:
1129 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1135 // Use this method rather than setting the properties
1136 // directly so that all the necessary properties can
1137 // be changed in harmony with each other. Maybe the
1138 // fields should be in a seperate class to help enforce
1140 private void SetProperties (
1141 XmlNodeType nodeType,
1143 bool isEmptyElement,
1145 bool clearAttributes)
1147 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1148 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1149 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1152 private void SetProperties (
1154 XmlNodeType nodeType,
1156 bool isEmptyElement,
1158 bool clearAttributes)
1160 this.valueBuilderAvailable = false;
1162 token.NodeType = nodeType;
1164 token.IsEmptyElement = isEmptyElement;
1165 token.Value = value;
1166 this.elementDepth = depth;
1168 if (clearAttributes)
1174 private void SetProperties (
1175 XmlNodeType nodeType,
1177 bool isEmptyElement,
1178 bool clearAttributes,
1179 StringBuilder value) {
1180 SetProperties (nodeType, name, isEmptyElement, (string)null, clearAttributes);
1181 this.valueBuilderAvailable = true;
1182 this.valueBuilder = value;
1185 private void ClearAttributes ()
1187 for (int i = 0; i < attributeCount; i++)
1188 attributeTokens [i].Clear ();
1190 currentAttribute = -1;
1191 currentAttributeValue = -1;
1194 private int PeekChar ()
1197 // return reader.Peek ();
1200 return peekChars [peekCharsIndex];
1205 peek_char = reader.Read ();
1206 if (peek_char >= 0xD800 && peek_char <= 0xDBFF) {
1207 int i = reader.Read ();
1208 if (i >= 0xDC00 && i <= 0xDFFF)
1217 private int ReadChar ()
1222 ch = peekChars [peekCharsIndex++];
1223 if (peekChars [peekCharsIndex] == 0)
1224 hasPeekChars = false;
1225 } else if (has_peek) {
1229 ch = reader.Read ();
1230 if (ch >= 0xD800 && ch <= 0xDBFF) {
1231 int i = reader.Read ();
1232 if (i > 0xDC00 && i <= 0xDFFF)
1245 if (ch < Char.MaxValue)
1246 currentTag.Append ((char) ch);
1248 currentTag.Append (ExpandSurrogateChar (ch));
1252 private string ExpandSurrogateChar (int ch)
1254 if (ch < Char.MaxValue)
1255 return ((char) peek_char).ToString ();
1257 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1258 return new string (tmp);
1262 // This should really keep track of some state so
1263 // that it's not possible to have more than one document
1264 // element or text outside of the document element.
1265 private bool ReadContent ()
1267 currentTag.Length = 0;
1269 parserContext.NamespaceManager.PopScope ();
1273 if (returnEntityReference)
1274 SetEntityReferenceProperties ();
1276 switch (PeekChar ()) {
1281 case '\r': goto case ' ';
1282 case '\n': goto case ' ';
1283 case '\t': goto case ' ';
1285 if (whitespaceHandling == WhitespaceHandling.All ||
1286 whitespaceHandling == WhitespaceHandling.Significant)
1290 return ReadContent ();
1294 readState = ReadState.EndOfFile;
1296 XmlNodeType.None, // nodeType
1297 String.Empty, // name
1298 false, // isEmptyElement
1299 (string) null, // value
1300 true // clearAttributes
1303 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1311 return this.ReadState != ReadState.EndOfFile;
1314 private void SetEntityReferenceProperties ()
1316 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1317 if (this.isStandalone)
1318 if (DTD == null || decl == null || !decl.IsInternalSubset)
1319 throw new XmlException (this as IXmlLineInfo,
1320 "Standalone document must not contain any references to an non-internally declared entity.");
1321 if (decl != null && decl.NotationName != null)
1322 throw new XmlException (this as IXmlLineInfo,
1323 "Reference to any unparsed entities is not allowed here.");
1326 XmlNodeType.EntityReference, // nodeType
1327 entityReferenceName, // name
1328 false, // isEmptyElement
1329 (string) null, // value
1330 true // clearAttributes
1333 returnEntityReference = false;
1334 entityReferenceName = String.Empty;
1337 // The leading '<' has already been consumed.
1338 private void ReadTag ()
1340 switch (PeekChar ())
1348 ReadProcessingInstruction ();
1360 // The leading '<' has already been consumed.
1361 private void ReadStartTag ()
1363 if (currentState == XmlNodeType.EndElement)
1364 throw new XmlException (this as IXmlLineInfo,
1365 "Element cannot appear in this state.");
1366 currentState = XmlNodeType.Element;
1368 parserContext.NamespaceManager.PushScope ();
1370 string name = ReadName ();
1371 if (currentState == XmlNodeType.EndElement)
1372 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1374 bool isEmptyElement = false;
1379 if (XmlChar.IsFirstNameChar (PeekChar ()))
1380 ReadAttributes (false);
1381 cursorToken = this.currentToken;
1384 for (int i = 0; i < attributeCount; i++)
1385 attributeTokens [i].FillNames ();
1388 for (int i = 0; i < attributeCount; i++)
1389 for (int j = i + 1; j < attributeCount; j++)
1390 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1391 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1392 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1393 throw new XmlException (this as IXmlLineInfo,
1394 "Attribute name and qualified name must be identical.");
1396 string baseUri = GetAttribute ("xml:base");
1397 if (baseUri != null)
1398 parserContext.BaseURI = baseUri;
1399 string xmlLang = GetAttribute ("xml:lang");
1400 if (xmlLang != null)
1401 parserContext.XmlLang = xmlLang;
1402 string xmlSpaceAttr = GetAttribute ("xml:space");
1403 if (xmlSpaceAttr != null) {
1404 if (xmlSpaceAttr == "preserve")
1405 parserContext.XmlSpace = XmlSpace.Preserve;
1406 else if (xmlSpaceAttr == "default")
1407 parserContext.XmlSpace = XmlSpace.Default;
1409 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1411 if (PeekChar () == '/') {
1413 isEmptyElement = true;
1418 elementStack.Push (name);
1419 parserContext.PushScope ();
1425 XmlNodeType.Element, // nodeType
1427 isEmptyElement, // isEmptyElement
1428 (string) null, // value
1429 false // clearAttributes
1433 // The reader is positioned on the first character
1434 // of the element's name.
1435 private void ReadEndTag ()
1437 if (currentState != XmlNodeType.Element)
1438 throw new XmlException (this as IXmlLineInfo,
1439 "End tag cannot appear in this state.");
1441 string name = ReadName ();
1442 if (elementStack.Count == 0)
1443 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1444 string expected = (string)elementStack.Pop();
1445 if (expected != name)
1446 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1447 parserContext.PopScope ();
1449 ExpectAfterWhitespace ('>');
1454 XmlNodeType.EndElement, // nodeType
1456 false, // isEmptyElement
1457 (string) null, // value
1458 true // clearAttributes
1464 private void AppendNameChar (int ch)
1466 CheckNameCapacity ();
1467 if (ch < Char.MaxValue)
1468 nameBuffer [nameLength++] = (char) ch;
1470 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1471 CheckNameCapacity ();
1472 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1476 private void CheckNameCapacity ()
1478 if (nameLength == nameCapacity) {
1479 nameCapacity = nameCapacity * 2;
1480 char [] oldNameBuffer = nameBuffer;
1481 nameBuffer = new char [nameCapacity];
1482 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1486 private string CreateNameString ()
1488 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1491 private void AppendValueChar (int ch)
1493 if (ch < Char.MaxValue)
1494 valueBuffer.Append ((char) ch);
1496 valueBuffer.Append (ExpandSurrogateChar (ch));
1499 private string CreateValueString ()
1501 return valueBuffer.ToString ();
1504 private void ClearValueBuffer ()
1506 valueBuffer.Length = 0;
1509 // The reader is positioned on the first character
1511 private void ReadText (bool notWhitespace)
1513 if (currentState != XmlNodeType.Element)
1514 throw new XmlException (this as IXmlLineInfo,
1515 "Text node cannot appear in this state.");
1518 ClearValueBuffer ();
1520 int ch = PeekChar ();
1521 bool previousWasCloseBracket = false;
1523 while (ch != '<' && ch != -1) {
1526 ch = ReadReference (false);
1527 if (returnEntityReference) // Returns -1 if char validation should not be done
1533 if (normalization && XmlChar.IsInvalid (ch))
1534 throw new XmlException (this, "Not allowed character was found.");
1535 AppendValueChar (ch);
1539 if (previousWasCloseBracket)
1540 if (PeekChar () == '>')
1541 throw new XmlException (this as IXmlLineInfo,
1542 "Inside text content, character sequence ']]>' is not allowed.");
1543 previousWasCloseBracket = true;
1545 else if (previousWasCloseBracket)
1546 previousWasCloseBracket = false;
1548 notWhitespace = true;
1551 if (returnEntityReference && valueBuffer.Length == 0) {
1552 SetEntityReferenceProperties ();
1554 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1555 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1557 nodeType, // nodeType
1558 String.Empty, // name
1559 false, // isEmptyElement
1560 true, // clearAttributes
1561 valueBuffer // value
1566 // The leading '&' has already been consumed.
1567 // Returns true if the entity reference isn't a simple
1568 // character reference or one of the predefined entities.
1569 // This allows the ReadText method to break so that the
1570 // next call to Read will return the EntityReference node.
1571 private int ReadReference (bool ignoreEntityReferences)
1573 if (PeekChar () == '#') {
1575 return ReadCharacterReference ();
1577 return ReadEntityReference (ignoreEntityReferences);
1580 private int ReadCharacterReference ()
1584 if (PeekChar () == 'x') {
1587 while (PeekChar () != ';' && PeekChar () != -1) {
1588 int ch = ReadChar ();
1590 if (ch >= '0' && ch <= '9')
1591 value = (value << 4) + ch - '0';
1592 else if (ch >= 'A' && ch <= 'F')
1593 value = (value << 4) + ch - 'A' + 10;
1594 else if (ch >= 'a' && ch <= 'f')
1595 value = (value << 4) + ch - 'a' + 10;
1597 throw new XmlException (this as IXmlLineInfo,
1599 "invalid hexadecimal digit: {0} (#x{1:X})",
1604 while (PeekChar () != ';' && PeekChar () != -1) {
1605 int ch = ReadChar ();
1607 if (ch >= '0' && ch <= '9')
1608 value = value * 10 + ch - '0';
1610 throw new XmlException (this as IXmlLineInfo,
1612 "invalid decimal digit: {0} (#x{1:X})",
1620 // There is no way to save surrogate pairs...
1621 if (normalization && XmlChar.IsInvalid (value))
1622 throw new XmlException (this as IXmlLineInfo,
1623 "Referenced character was not allowed in XML.");
1627 // Returns -1 if it should not be validated.
1628 // Real EOF must not be detected here.
1629 private int ReadEntityReference (bool ignoreEntityReferences)
1633 int ch = PeekChar ();
1635 while (ch != ';' && ch != -1) {
1636 AppendNameChar (ReadChar ());
1642 string name = CreateNameString ();
1643 if (!XmlChar.IsName (name))
1644 throw new XmlException (this as IXmlLineInfo,
1645 "Invalid entity reference name was found.");
1647 int predefined = XmlChar.GetPredefinedEntity (name);
1648 if (predefined >= 0)
1649 // AppendValueChar (predefined);
1652 if (ignoreEntityReferences) {
1653 AppendValueChar ('&');
1655 foreach (char ch2 in name) {
1656 AppendValueChar (ch2);
1659 AppendValueChar (';');
1661 returnEntityReference = true;
1662 entityReferenceName = name;
1668 // The reader is positioned on the first character of
1669 // the attribute name.
1670 private void ReadAttributes (bool endsWithQuestion)
1673 bool requireWhitespace = false;
1674 currentAttribute = -1;
1675 currentAttributeValue = -1;
1678 if (!SkipWhitespace () && requireWhitespace)
1679 throw new XmlException ("Unexpected token. Name is required here.");
1681 IncrementAttributeToken ();
1682 currentAttributeToken.LineNumber = line;
1683 currentAttributeToken.LinePosition = column;
1685 currentAttributeToken.LocalName =
1686 currentAttributeToken.Name = ReadName ();
1687 ExpectAfterWhitespace ('=');
1689 ReadAttributeValueTokens (-1);
1692 if (currentAttributeToken.Name == "xmlns")
1693 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1694 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1695 string nsPrefix = currentAttributeToken.Name.Substring (6);
1696 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1699 if (!SkipWhitespace ())
1700 requireWhitespace = true;
1701 peekChar = PeekChar ();
1702 if (endsWithQuestion) {
1703 if (peekChar == '?')
1706 else if (peekChar == '/' || peekChar == '>')
1708 } while (peekChar != -1);
1710 currentAttribute = -1;
1711 currentAttributeValue = -1;
1714 private void AddAttribute (string name, string value)
1716 IncrementAttributeToken ();
1717 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1718 ati.Name = "SYSTEM";
1720 IncrementAttributeValueToken ();
1721 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1723 SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1727 private void IncrementAttributeToken ()
1730 if (attributeTokens.Length == currentAttribute) {
1731 XmlAttributeTokenInfo [] newArray =
1732 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1733 attributeTokens.CopyTo (newArray, 0);
1734 attributeTokens = newArray;
1736 if (attributeTokens [currentAttribute] == null)
1737 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1738 currentAttributeToken = attributeTokens [currentAttribute];
1739 currentAttributeToken.Clear ();
1742 private void IncrementAttributeValueToken ()
1744 ClearValueBuffer ();
1745 currentAttributeValue++;
1746 if (attributeValueTokens.Length == currentAttributeValue) {
1747 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1748 attributeValueTokens.CopyTo (newArray, 0);
1749 attributeValueTokens = newArray;
1751 if (attributeValueTokens [currentAttributeValue] == null)
1752 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this);
1753 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1754 currentAttributeValueToken.Clear ();
1757 private void ReadAttributeValueTokens (int dummyQuoteChar)
1759 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1761 if (quoteChar != '\'' && quoteChar != '\"')
1762 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1763 currentAttributeToken.QuoteChar = (char) quoteChar;
1765 IncrementAttributeValueToken ();
1766 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1767 currentAttributeValueToken.LineNumber = line;
1768 currentAttributeValueToken.LinePosition = column;
1770 bool incrementToken = false;
1771 bool isNewToken = true;
1776 if (ch == quoteChar)
1779 if (incrementToken) {
1780 IncrementAttributeValueToken ();
1781 currentAttributeValueToken.LineNumber = line;
1782 currentAttributeValueToken.LinePosition = column;
1783 incrementToken = false;
1790 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1792 if (dummyQuoteChar < 0)
1793 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1794 else // Attribute value constructor.
1798 int startPosition = currentTag.Length - 1;
1799 if (PeekChar () == '#') {
1801 ch = ReadCharacterReference ();
1802 if (normalization && XmlChar.IsInvalid (ch))
1803 throw new XmlException (this as IXmlLineInfo,
1804 "Not allowed character was found.");
1805 AppendValueChar (ch);
1808 // Check XML 1.0 section 3.1 WFC.
1809 string entName = ReadName ();
1811 int predefined = XmlChar.GetPredefinedEntity (entName);
1812 if (predefined < 0) {
1813 DTDEntityDeclaration entDecl =
1814 DTD == null ? null : DTD.EntityDecls [entName];
1815 if (DTD != null && resolver != null && entDecl == null)
1816 throw new XmlException (this as IXmlLineInfo, "Entity declaration does not exist.");
1817 if (entDecl != null && entDecl.HasExternalReference)
1818 throw new XmlException (this as IXmlLineInfo,
1819 "Reference to external entities is not allowed in the value of an attribute.");
1820 if (isStandalone && !entDecl.IsInternalSubset)
1821 throw new XmlException (this as IXmlLineInfo,
1822 "Reference to external entities is not allowed in the value of an attribute.");
1823 if (entDecl != null && entDecl.EntityValue.IndexOf ('<') >= 0)
1824 throw new XmlException (this as IXmlLineInfo,
1825 "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1826 currentAttributeValueToken.Value = CreateValueString ();
1827 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1829 IncrementAttributeValueToken ();
1830 currentAttributeValueToken.Name = entName;
1831 currentAttributeValueToken.Value = String.Empty;
1832 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1833 incrementToken = true;
1836 AppendValueChar (predefined);
1839 if (normalization && XmlChar.IsInvalid (ch))
1840 throw new XmlException (this, "Invalid character was found.");
1841 AppendValueChar (ch);
1847 if (!incrementToken) {
1848 currentAttributeValueToken.Value = CreateValueString ();
1849 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1851 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1855 // The reader is positioned on the first character
1858 // It may be xml declaration or processing instruction.
1859 private void ReadProcessingInstruction ()
1861 string target = ReadName ();
1862 if (target == "xml") {
1863 ReadXmlDeclaration ();
1865 } else if (target.ToLower () == "xml")
1866 throw new XmlException (this as IXmlLineInfo,
1867 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1869 if (currentState == XmlNodeType.None)
1870 currentState = XmlNodeType.XmlDeclaration;
1872 if (!SkipWhitespace ())
1873 if (PeekChar () != '?')
1874 throw new XmlException (this as IXmlLineInfo,
1875 "Invalid processing instruction name was found.");
1877 ClearValueBuffer ();
1879 while (PeekChar () != -1) {
1880 int ch = ReadChar ();
1882 if (ch == '?' && PeekChar () == '>') {
1887 if (normalization && XmlChar.IsInvalid (ch))
1888 throw new XmlException (this, "Invalid character was found.");
1889 AppendValueChar (ch);
1893 XmlNodeType.ProcessingInstruction, // nodeType
1895 false, // isEmptyElement
1896 true, // clearAttributes
1897 valueBuffer // value
1901 // The reader is positioned after "<?xml "
1902 private void ReadXmlDeclaration ()
1904 if (currentState != XmlNodeType.None) {
1905 throw new XmlException (this as IXmlLineInfo,
1906 "XML declaration cannot appear in this state.");
1908 currentState = XmlNodeType.XmlDeclaration;
1912 ReadAttributes (true); // They must have "version."
1913 string version = GetAttribute ("version");
1915 string message = null;
1917 if (attributeTokens [0].Name != "version" || version != "1.0")
1918 message = "Version 1.0 declaration is required in XML Declaration.";
1919 else if (attributeCount > 1 &&
1920 (attributeTokens [1].Name != "encoding" &&
1921 attributeTokens [1].Name != "standalone"))
1922 message = "Invalid Xml Declaration markup was found.";
1923 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1924 message = "Invalid Xml Declaration markup was found.";
1925 string sa = GetAttribute ("standalone");
1926 if (sa != null && sa != "yes" && sa != "no")
1927 message = "Only 'yes' or 'no' is allowed for standalone.";
1929 this.isStandalone = (sa == "yes");
1931 if (message != null)
1932 throw new XmlException (this as IXmlLineInfo, message);
1935 XmlNodeType.XmlDeclaration, // nodeType
1937 false, // isEmptyElement
1938 currentTag.ToString (6, currentTag.Length - 6), // value
1939 false // clearAttributes
1945 internal void SkipTextDeclaration ()
1947 this.currentState = XmlNodeType.Element;
1949 if (PeekChar () != '<')
1953 peekChars [0] = '<';
1955 if (PeekChar () != '?') {
1956 hasPeekChars = true;
1960 peekChars [1] = '?';
1962 for (int i = 2; i < 6; i++) {
1963 if (PeekChar () == 0)
1966 peekChars [i] = (char) ReadChar ();
1968 if (new string (peekChars, 2, 4) != "xml ") {
1969 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1970 throw new XmlException (this as IXmlLineInfo,
1971 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1973 hasPeekChars = true;
1977 for (int i = 0; i < 6; i++)
1978 peekChars [i] = '\0';
1983 if (PeekChar () == 'v') {
1985 ExpectAfterWhitespace ('=');
1987 int quoteChar = ReadChar ();
1988 char [] expect1_0 = new char [3];
1989 int versionLength = 0;
1990 switch (quoteChar) {
1993 while (PeekChar () != quoteChar) {
1994 if (PeekChar () == -1)
1995 throw new XmlException (this as IXmlLineInfo,
1996 "Invalid version declaration inside text declaration.");
1997 else if (versionLength == 3)
1998 throw new XmlException (this as IXmlLineInfo,
1999 "Invalid version number inside text declaration.");
2001 expect1_0 [versionLength] = (char) ReadChar ();
2003 if (versionLength == 3 && new String (expect1_0) != "1.0")
2004 throw new XmlException (this as IXmlLineInfo,
2005 "Invalid version number inside text declaration.");
2012 throw new XmlException (this as IXmlLineInfo,
2013 "Invalid version declaration inside text declaration.");
2017 if (PeekChar () == 'e') {
2018 Expect ("encoding");
2019 ExpectAfterWhitespace ('=');
2021 int quoteChar = ReadChar ();
2022 switch (quoteChar) {
2025 while (PeekChar () != quoteChar)
2026 if (ReadChar () == -1)
2027 throw new XmlException (this as IXmlLineInfo,
2028 "Invalid encoding declaration inside text declaration.");
2033 throw new XmlException (this as IXmlLineInfo,
2034 "Invalid encoding declaration inside text declaration.");
2036 // Encoding value should be checked inside XmlInputStream.
2039 throw new XmlException (this as IXmlLineInfo,
2040 "Encoding declaration is mandatory in text declaration.");
2045 // The reader is positioned on the first character after
2046 // the leading '<!'.
2047 private void ReadDeclaration ()
2049 int ch = PeekChar ();
2067 throw new XmlException (this as IXmlLineInfo,
2068 "Unexpected declaration markup was found.");
2072 // The reader is positioned on the first character after
2073 // the leading '<!--'.
2074 private void ReadComment ()
2076 if (currentState == XmlNodeType.None)
2077 currentState = XmlNodeType.XmlDeclaration;
2079 ClearValueBuffer ();
2081 while (PeekChar () != -1) {
2082 int ch = ReadChar ();
2084 if (ch == '-' && PeekChar () == '-') {
2087 if (PeekChar () != '>')
2088 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2094 if (XmlChar.IsInvalid (ch))
2095 throw new XmlException (this as IXmlLineInfo,
2096 "Not allowed character was found.");
2098 AppendValueChar (ch);
2102 XmlNodeType.Comment, // nodeType
2103 String.Empty, // name
2104 false, // isEmptyElement
2105 true, // clearAttributes
2106 valueBuffer // value
2110 // The reader is positioned on the first character after
2111 // the leading '<![CDATA['.
2112 private void ReadCDATA ()
2114 if (currentState != XmlNodeType.Element)
2115 throw new XmlException (this as IXmlLineInfo,
2116 "CDATA section cannot appear in this state.");
2118 ClearValueBuffer ();
2122 while (PeekChar () != -1) {
2127 if (ch == ']' && PeekChar () == ']') {
2128 ch = ReadChar (); // ']'
2130 if (PeekChar () == '>') {
2135 // AppendValueChar (']');
2136 // AppendValueChar (']');
2137 // ch = ReadChar ();
2140 if (normalization && XmlChar.IsInvalid (ch))
2141 throw new XmlException (this, "Invalid character was found.");
2143 AppendValueChar (ch);
2147 XmlNodeType.CDATA, // nodeType
2148 String.Empty, // name
2149 false, // isEmptyElement
2150 true, // clearAttributes
2151 valueBuffer // value
2155 // The reader is positioned on the first character after
2156 // the leading '<!DOCTYPE'.
2157 private void ReadDoctypeDecl ()
2159 switch (currentState) {
2160 case XmlNodeType.DocumentType:
2161 case XmlNodeType.Element:
2162 case XmlNodeType.EndElement:
2163 throw new XmlException (this as IXmlLineInfo,
2164 "Document type cannot appear in this state.");
2166 currentState = XmlNodeType.DocumentType;
2168 string doctypeName = null;
2169 string publicId = null;
2170 string systemId = null;
2171 int intSubsetStartLine = 0;
2172 int intSubsetStartColumn = 0;
2175 doctypeName = ReadName ();
2180 systemId = ReadSystemLiteral (true);
2183 publicId = ReadPubidLiteral ();
2184 if (!SkipWhitespace ())
2185 throw new XmlException (this as IXmlLineInfo,
2186 "Whitespace is required between PUBLIC id and SYSTEM id.");
2187 systemId = ReadSystemLiteral (false);
2193 if(PeekChar () == '[')
2195 // read markupdecl etc. or end of decl
2197 intSubsetStartLine = this.LineNumber;
2198 intSubsetStartColumn = this.LinePosition;
2199 int startPos = currentTag.Length;
2200 ReadInternalSubset ();
2201 int endPos = currentTag.Length - 1;
2202 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
2204 // end of DOCTYPE decl.
2205 ExpectAfterWhitespace ('>');
2207 GenerateDTDObjectModel (doctypeName, publicId,
2208 systemId, parserContext.InternalSubset,
2209 intSubsetStartLine, intSubsetStartColumn);
2211 // set properties for <!DOCTYPE> node
2213 XmlNodeType.DocumentType, // nodeType
2214 doctypeName, // name
2215 false, // isEmptyElement
2216 parserContext.InternalSubset, // value
2217 true // clearAttributes
2220 if (publicId != null)
2221 AddAttribute ("PUBLIC", publicId);
2222 if (systemId != null)
2223 AddAttribute ("SYSTEM", systemId);
2224 currentAttribute = currentAttributeValue = -1;
2227 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2228 string systemId, string internalSubset)
2230 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2233 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2234 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2237 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2238 DTD.BaseURI = BaseURI;
2240 DTD.PublicId = publicId;
2241 DTD.SystemId = systemId;
2242 DTD.InternalSubset = internalSubset;
2243 DTD.XmlResolver = resolver;
2244 DTD.IsStandalone = isStandalone;
2245 DTD.LineNumber = line;
2246 DTD.LinePosition = column;
2248 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2249 dr.Normalization = this.normalization;
2250 #if DTD_HANDLE_EVENTS
2251 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2253 return dr.GenerateDTDObjectModel ();
2256 private void OnValidationEvent (object o, ValidationEventArgs e)
2258 #if DTD_HANDLE_EVENTS
2259 if (ValidationEventHandler != null)
2260 // Override object as this.
2261 ValidationEventHandler (this, e);
2265 private enum DtdInputState
2278 private class DtdInputStateStack
2280 Stack intern = new Stack ();
2281 public DtdInputStateStack ()
2283 Push (DtdInputState.Free);
2286 public DtdInputState Peek ()
2288 return (DtdInputState) intern.Peek ();
2291 public DtdInputState Pop ()
2293 return (DtdInputState) intern.Pop ();
2296 public void Push (DtdInputState val)
2303 DtdInputStateStack stateStack = new DtdInputStateStack ();
2304 DtdInputState State {
2305 get { return stateStack.Peek (); }
2308 // Simply read but not generate any result.
2309 private void ReadInternalSubset ()
2311 bool continueParse = true;
2313 while (continueParse) {
2314 switch (ReadChar ()) {
2317 case DtdInputState.Free:
2318 continueParse = false;
2320 case DtdInputState.InsideDoubleQuoted:
2322 case DtdInputState.InsideSingleQuoted:
2325 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2329 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2331 if (State == DtdInputState.InsideDoubleQuoted ||
2332 State == DtdInputState.InsideSingleQuoted)
2333 continue; // well-formed
2334 switch (ReadChar ()) {
2336 stateStack.Push (DtdInputState.PI);
2339 switch (ReadChar ()) {
2341 switch (ReadChar ()) {
2344 stateStack.Push (DtdInputState.ElementDecl);
2348 stateStack.Push (DtdInputState.EntityDecl);
2351 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2356 stateStack.Push (DtdInputState.AttlistDecl);
2360 stateStack.Push (DtdInputState.NotationDecl);
2364 stateStack.Push (DtdInputState.Comment);
2369 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2373 if (State == DtdInputState.InsideSingleQuoted)
2375 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2376 stateStack.Push (DtdInputState.InsideSingleQuoted);
2379 if (State == DtdInputState.InsideDoubleQuoted)
2381 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2382 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2386 case DtdInputState.ElementDecl:
2387 goto case DtdInputState.NotationDecl;
2388 case DtdInputState.AttlistDecl:
2389 goto case DtdInputState.NotationDecl;
2390 case DtdInputState.EntityDecl:
2391 goto case DtdInputState.NotationDecl;
2392 case DtdInputState.NotationDecl:
2395 case DtdInputState.InsideDoubleQuoted:
2397 case DtdInputState.InsideSingleQuoted:
2398 continue; // well-formed
2399 case DtdInputState.Comment:
2402 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2406 if (State == DtdInputState.PI) {
2407 if (ReadChar () == '>')
2412 if (State == DtdInputState.Comment) {
2413 if (PeekChar () == '-') {
2421 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2422 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2428 // The reader is positioned on the first 'S' of "SYSTEM".
2429 private string ReadSystemLiteral (bool expectSYSTEM)
2433 if (!SkipWhitespace ())
2434 throw new XmlException (this as IXmlLineInfo,
2435 "Whitespace is required after 'SYSTEM'.");
2439 int quoteChar = ReadChar (); // apos or quot
2440 int startPos = currentTag.Length;
2442 ClearValueBuffer ();
2443 while (c != quoteChar) {
2446 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2448 AppendValueChar (c);
2450 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2453 private string ReadPubidLiteral()
2456 if (!SkipWhitespace ())
2457 throw new XmlException (this as IXmlLineInfo,
2458 "Whitespace is required after 'PUBLIC'.");
2459 int quoteChar = ReadChar ();
2460 int startPos = currentTag.Length;
2462 ClearValueBuffer ();
2463 while(c != quoteChar)
2466 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2467 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2468 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2470 AppendValueChar (c);
2472 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2475 // The reader is positioned on the first character
2477 private string ReadName ()
2479 return ReadNameOrNmToken(false);
2482 // The reader is positioned on the first character
2484 private string ReadNmToken ()
2486 return ReadNameOrNmToken(true);
2489 private string ReadNameOrNmToken (bool isNameToken)
2491 int ch = PeekChar ();
2493 if (!XmlChar.IsNameChar (ch))
2494 throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char) ch));
2497 if (!XmlChar.IsFirstNameChar (ch))
2498 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2503 AppendNameChar (ReadChar ());
2505 while (XmlChar.IsNameChar (PeekChar ())) {
2506 AppendNameChar (ReadChar ());
2509 return CreateNameString ();
2512 // Read the next character and compare it against the
2513 // specified character.
2514 private void Expect (int expected)
2516 int ch = ReadChar ();
2518 if (ch != expected) {
2519 throw new XmlException (this as IXmlLineInfo,
2521 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2529 private void Expect (string expected)
2531 int len = expected.Length;
2532 for(int i=0; i< len; i++)
2533 Expect (expected[i]);
2536 private void ExpectAfterWhitespace (char c)
2539 int i = ReadChar ();
2540 if (XmlChar.IsWhitespace (i))
2543 throw new XmlException (String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2548 // Does not consume the first non-whitespace character.
2549 private bool SkipWhitespace ()
2551 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2552 while (XmlChar.IsWhitespace (PeekChar ()))
2557 private void ReadWhitespace ()
2559 if (currentState == XmlNodeType.None)
2560 currentState = XmlNodeType.XmlDeclaration;
2562 ClearValueBuffer ();
2563 int ch = PeekChar ();
2565 AppendValueChar (ReadChar ());
2566 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2568 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2571 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2572 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2573 SetProperties (nodeType,
2580 return; // (PeekChar () != -1);
2583 private byte GetBase64Byte (char ch)
2593 if (ch >= 'A' && ch <= 'Z')
2594 return (byte) (ch - 'A');
2595 else if (ch >= 'a' && ch <= 'z')
2596 return (byte) (ch - 'a' + 26);
2597 else if (ch >= '0' && ch <= '9')
2598 return (byte) (ch - '0' + 52);
2600 throw new XmlException ("Invalid Base64 character was found.");
2604 // Returns -1 if it should throw an error.
2605 private int ReadCharsInternal (char [] buffer, int offset, int length)
2607 shouldSkipUntilEndTag = true;
2610 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2611 else if (length < 0)
2612 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2613 else if (buffer.Length < offset + length)
2614 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2616 if (NodeType != XmlNodeType.Element)
2619 int bufIndex = offset;
2620 for (int i = 0; i < length; i++) {
2621 int c = PeekChar ();
2624 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2627 if (PeekChar () != '/') {
2628 buffer [bufIndex++] = '<';
2631 // Seems to skip immediate EndElement
2633 string name = ReadName ();
2634 if (name != (string) this.elementStack.Peek ()) {
2635 if (i + 1 < length) {
2636 buffer [bufIndex++] = '<';
2637 buffer [bufIndex++] = '/';
2639 for (int n = 0; n < name.Length && i + n + 1 < length; n++)
2640 buffer [bufIndex++] = name [n];
2645 this.elementStack.Pop ();
2646 shouldSkipUntilEndTag = false;
2651 if (c < Char.MaxValue)
2652 buffer [bufIndex++] = (char) c;
2654 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2655 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2663 private bool ReadUntilEndTag ()
2670 throw new XmlException (this as IXmlLineInfo,
2671 "Unexpected end of xml.");
2673 if (PeekChar () != '/')
2676 string name = ReadName ();
2677 if (name != (string) this.elementStack.Peek ())
2681 this.elementStack.Pop ();