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), (char) (ch % 0x10000 + 0xDC00)};
1258 return tmp.ToString ();
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);
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.Name = ReadName ();
1686 ExpectAfterWhitespace ('=');
1688 ReadAttributeValueTokens (-1);
1691 if (currentAttributeToken.Name == "xmlns")
1692 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1693 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1694 string nsPrefix = currentAttributeToken.Name.Substring (6);
1695 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1698 if (!SkipWhitespace ())
1699 requireWhitespace = true;
1700 peekChar = PeekChar ();
1701 if (endsWithQuestion) {
1702 if (peekChar == '?')
1705 else if (peekChar == '/' || peekChar == '>')
1707 } while (peekChar != -1);
1709 currentAttribute = -1;
1710 currentAttributeValue = -1;
1713 private void AddAttribute (string name, string value)
1715 IncrementAttributeToken ();
1716 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1717 ati.Name = "SYSTEM";
1719 IncrementAttributeValueToken ();
1720 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1722 SetProperties (vti, XmlNodeType.Text, name, false, value, false);
1726 private void IncrementAttributeToken ()
1729 if (attributeTokens.Length == currentAttribute) {
1730 XmlAttributeTokenInfo [] newArray =
1731 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1732 attributeTokens.CopyTo (newArray, 0);
1733 attributeTokens = newArray;
1735 if (attributeTokens [currentAttribute] == null)
1736 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1737 currentAttributeToken = attributeTokens [currentAttribute];
1738 currentAttributeToken.Clear ();
1741 private void IncrementAttributeValueToken ()
1743 ClearValueBuffer ();
1744 currentAttributeValue++;
1745 if (attributeValueTokens.Length == currentAttributeValue) {
1746 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1747 attributeValueTokens.CopyTo (newArray, 0);
1748 attributeValueTokens = newArray;
1750 if (attributeValueTokens [currentAttributeValue] == null)
1751 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this);
1752 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1753 currentAttributeValueToken.Clear ();
1756 private void ReadAttributeValueTokens (int dummyQuoteChar)
1758 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1760 if (quoteChar != '\'' && quoteChar != '\"')
1761 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1762 currentAttributeToken.QuoteChar = (char) quoteChar;
1764 IncrementAttributeValueToken ();
1765 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1766 currentAttributeValueToken.LineNumber = line;
1767 currentAttributeValueToken.LinePosition = column;
1769 bool incrementToken = false;
1770 bool isNewToken = true;
1775 if (ch == quoteChar)
1778 if (incrementToken) {
1779 IncrementAttributeValueToken ();
1780 currentAttributeValueToken.LineNumber = line;
1781 currentAttributeValueToken.LinePosition = column;
1782 incrementToken = false;
1789 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1791 if (dummyQuoteChar < 0)
1792 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1793 else // Attribute value constructor.
1797 int startPosition = currentTag.Length - 1;
1798 if (PeekChar () == '#') {
1800 ch = ReadCharacterReference ();
1801 if (normalization && XmlChar.IsInvalid (ch))
1802 throw new XmlException (this as IXmlLineInfo,
1803 "Not allowed character was found.");
1804 AppendValueChar (ch);
1807 // Check XML 1.0 section 3.1 WFC.
1808 string entName = ReadName ();
1810 int predefined = XmlChar.GetPredefinedEntity (entName);
1811 if (predefined < 0) {
1812 DTDEntityDeclaration entDecl =
1813 DTD == null ? null : DTD.EntityDecls [entName];
1814 if (DTD != null && resolver != null && entDecl == null)
1815 throw new XmlException (this as IXmlLineInfo, "Entity declaration does not exist.");
1816 if (entDecl != null && entDecl.HasExternalReference)
1817 throw new XmlException (this as IXmlLineInfo,
1818 "Reference to external entities is not allowed in the value of an attribute.");
1819 if (isStandalone && !entDecl.IsInternalSubset)
1820 throw new XmlException (this as IXmlLineInfo,
1821 "Reference to external entities is not allowed in the value of an attribute.");
1822 if (entDecl != null && entDecl.EntityValue.IndexOf ('<') >= 0)
1823 throw new XmlException (this as IXmlLineInfo,
1824 "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1825 currentAttributeValueToken.Value = CreateValueString ();
1826 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1828 IncrementAttributeValueToken ();
1829 currentAttributeValueToken.Name = entName;
1830 currentAttributeValueToken.Value = String.Empty;
1831 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1832 incrementToken = true;
1835 AppendValueChar (predefined);
1838 if (normalization && XmlChar.IsInvalid (ch))
1839 throw new XmlException (this, "Invalid character was found.");
1840 AppendValueChar (ch);
1846 if (!incrementToken) {
1847 currentAttributeValueToken.Value = CreateValueString ();
1848 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1850 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1854 // The reader is positioned on the first character
1857 // It may be xml declaration or processing instruction.
1858 private void ReadProcessingInstruction ()
1860 string target = ReadName ();
1861 if (target == "xml") {
1862 ReadXmlDeclaration ();
1864 } else if (target.ToLower () == "xml")
1865 throw new XmlException (this as IXmlLineInfo,
1866 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1868 if (currentState == XmlNodeType.None)
1869 currentState = XmlNodeType.XmlDeclaration;
1871 if (!SkipWhitespace ())
1872 if (PeekChar () != '?')
1873 throw new XmlException (this as IXmlLineInfo,
1874 "Invalid processing instruction name was found.");
1876 ClearValueBuffer ();
1878 while (PeekChar () != -1) {
1879 int ch = ReadChar ();
1881 if (ch == '?' && PeekChar () == '>') {
1886 if (normalization && XmlChar.IsInvalid (ch))
1887 throw new XmlException (this, "Invalid character was found.");
1888 AppendValueChar (ch);
1892 XmlNodeType.ProcessingInstruction, // nodeType
1894 false, // isEmptyElement
1895 true, // clearAttributes
1896 valueBuffer // value
1900 // The reader is positioned after "<?xml "
1901 private void ReadXmlDeclaration ()
1903 if (currentState != XmlNodeType.None) {
1904 throw new XmlException (this as IXmlLineInfo,
1905 "XML declaration cannot appear in this state.");
1907 currentState = XmlNodeType.XmlDeclaration;
1911 ReadAttributes (true); // They must have "version."
1912 string version = GetAttribute ("version");
1914 string message = null;
1916 if (attributeTokens [0].Name != "version" || version != "1.0")
1917 message = "Version 1.0 declaration is required in XML Declaration.";
1918 else if (attributeCount > 1 &&
1919 (attributeTokens [1].Name != "encoding" &&
1920 attributeTokens [1].Name != "standalone"))
1921 message = "Invalid Xml Declaration markup was found.";
1922 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1923 message = "Invalid Xml Declaration markup was found.";
1924 string sa = GetAttribute ("standalone");
1925 if (sa != null && sa != "yes" && sa != "no")
1926 message = "Only 'yes' or 'no' is allowed for standalone.";
1928 this.isStandalone = (sa == "yes");
1930 if (message != null)
1931 throw new XmlException (this as IXmlLineInfo, message);
1934 XmlNodeType.XmlDeclaration, // nodeType
1936 false, // isEmptyElement
1937 currentTag.ToString (6, currentTag.Length - 6), // value
1938 false // clearAttributes
1944 internal void SkipTextDeclaration ()
1946 this.currentState = XmlNodeType.Element;
1948 if (PeekChar () != '<')
1952 peekChars [0] = '<';
1954 if (PeekChar () != '?') {
1955 hasPeekChars = true;
1959 peekChars [1] = '?';
1961 for (int i = 2; i < 6; i++) {
1962 if (PeekChar () == 0)
1965 peekChars [i] = (char) ReadChar ();
1967 if (new string (peekChars, 2, 4) != "xml ") {
1968 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1969 throw new XmlException (this as IXmlLineInfo,
1970 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1972 hasPeekChars = true;
1976 for (int i = 0; i < 6; i++)
1977 peekChars [i] = '\0';
1982 if (PeekChar () == 'v') {
1984 ExpectAfterWhitespace ('=');
1986 int quoteChar = ReadChar ();
1987 char [] expect1_0 = new char [3];
1988 int versionLength = 0;
1989 switch (quoteChar) {
1992 while (PeekChar () != quoteChar) {
1993 if (PeekChar () == -1)
1994 throw new XmlException (this as IXmlLineInfo,
1995 "Invalid version declaration inside text declaration.");
1996 else if (versionLength == 3)
1997 throw new XmlException (this as IXmlLineInfo,
1998 "Invalid version number inside text declaration.");
2000 expect1_0 [versionLength] = (char) ReadChar ();
2002 if (versionLength == 3 && new String (expect1_0) != "1.0")
2003 throw new XmlException (this as IXmlLineInfo,
2004 "Invalid version number inside text declaration.");
2011 throw new XmlException (this as IXmlLineInfo,
2012 "Invalid version declaration inside text declaration.");
2016 if (PeekChar () == 'e') {
2017 Expect ("encoding");
2018 ExpectAfterWhitespace ('=');
2020 int quoteChar = ReadChar ();
2021 switch (quoteChar) {
2024 while (PeekChar () != quoteChar)
2025 if (ReadChar () == -1)
2026 throw new XmlException (this as IXmlLineInfo,
2027 "Invalid encoding declaration inside text declaration.");
2032 throw new XmlException (this as IXmlLineInfo,
2033 "Invalid encoding declaration inside text declaration.");
2035 // Encoding value should be checked inside XmlInputStream.
2038 throw new XmlException (this as IXmlLineInfo,
2039 "Encoding declaration is mandatory in text declaration.");
2044 // The reader is positioned on the first character after
2045 // the leading '<!'.
2046 private void ReadDeclaration ()
2048 int ch = PeekChar ();
2066 throw new XmlException (this as IXmlLineInfo,
2067 "Unexpected declaration markup was found.");
2071 // The reader is positioned on the first character after
2072 // the leading '<!--'.
2073 private void ReadComment ()
2075 if (currentState == XmlNodeType.None)
2076 currentState = XmlNodeType.XmlDeclaration;
2078 ClearValueBuffer ();
2080 while (PeekChar () != -1) {
2081 int ch = ReadChar ();
2083 if (ch == '-' && PeekChar () == '-') {
2086 if (PeekChar () != '>')
2087 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2093 if (XmlChar.IsInvalid (ch))
2094 throw new XmlException (this as IXmlLineInfo,
2095 "Not allowed character was found.");
2097 AppendValueChar (ch);
2101 XmlNodeType.Comment, // nodeType
2102 String.Empty, // name
2103 false, // isEmptyElement
2104 true, // clearAttributes
2105 valueBuffer // value
2109 // The reader is positioned on the first character after
2110 // the leading '<![CDATA['.
2111 private void ReadCDATA ()
2113 if (currentState != XmlNodeType.Element)
2114 throw new XmlException (this as IXmlLineInfo,
2115 "CDATA section cannot appear in this state.");
2117 ClearValueBuffer ();
2121 while (PeekChar () != -1) {
2126 if (ch == ']' && PeekChar () == ']') {
2127 ch = ReadChar (); // ']'
2129 if (PeekChar () == '>') {
2134 // AppendValueChar (']');
2135 // AppendValueChar (']');
2136 // ch = ReadChar ();
2139 if (normalization && XmlChar.IsInvalid (ch))
2140 throw new XmlException (this, "Invalid character was found.");
2142 AppendValueChar (ch);
2146 XmlNodeType.CDATA, // nodeType
2147 String.Empty, // name
2148 false, // isEmptyElement
2149 true, // clearAttributes
2150 valueBuffer // value
2154 // The reader is positioned on the first character after
2155 // the leading '<!DOCTYPE'.
2156 private void ReadDoctypeDecl ()
2158 switch (currentState) {
2159 case XmlNodeType.DocumentType:
2160 case XmlNodeType.Element:
2161 case XmlNodeType.EndElement:
2162 throw new XmlException (this as IXmlLineInfo,
2163 "Document type cannot appear in this state.");
2165 currentState = XmlNodeType.DocumentType;
2167 string doctypeName = null;
2168 string publicId = null;
2169 string systemId = null;
2170 int intSubsetStartLine = 0;
2171 int intSubsetStartColumn = 0;
2174 doctypeName = ReadName ();
2179 systemId = ReadSystemLiteral (true);
2182 publicId = ReadPubidLiteral ();
2183 if (!SkipWhitespace ())
2184 throw new XmlException (this as IXmlLineInfo,
2185 "Whitespace is required between PUBLIC id and SYSTEM id.");
2186 systemId = ReadSystemLiteral (false);
2192 if(PeekChar () == '[')
2194 // read markupdecl etc. or end of decl
2196 intSubsetStartLine = this.LineNumber;
2197 intSubsetStartColumn = this.LinePosition;
2198 int startPos = currentTag.Length;
2199 ReadInternalSubset ();
2200 int endPos = currentTag.Length - 1;
2201 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
2203 // end of DOCTYPE decl.
2204 ExpectAfterWhitespace ('>');
2206 GenerateDTDObjectModel (doctypeName, publicId,
2207 systemId, parserContext.InternalSubset,
2208 intSubsetStartLine, intSubsetStartColumn);
2210 // set properties for <!DOCTYPE> node
2212 XmlNodeType.DocumentType, // nodeType
2213 doctypeName, // name
2214 false, // isEmptyElement
2215 parserContext.InternalSubset, // value
2216 true // clearAttributes
2219 if (publicId != null)
2220 AddAttribute ("PUBLIC", publicId);
2221 if (systemId != null)
2222 AddAttribute ("SYSTEM", systemId);
2225 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2226 string systemId, string internalSubset)
2228 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2231 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2232 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2235 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2236 DTD.BaseURI = BaseURI;
2238 DTD.PublicId = publicId;
2239 DTD.SystemId = systemId;
2240 DTD.InternalSubset = internalSubset;
2241 DTD.XmlResolver = resolver;
2242 DTD.IsStandalone = isStandalone;
2243 DTD.LineNumber = line;
2244 DTD.LinePosition = column;
2246 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2247 dr.Normalization = this.normalization;
2248 #if DTD_HANDLE_EVENTS
2249 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2251 return dr.GenerateDTDObjectModel ();
2254 private void OnValidationEvent (object o, ValidationEventArgs e)
2256 #if DTD_HANDLE_EVENTS
2257 if (ValidationEventHandler != null)
2258 // Override object as this.
2259 ValidationEventHandler (this, e);
2263 private enum DtdInputState
2276 private class DtdInputStateStack
2278 Stack intern = new Stack ();
2279 public DtdInputStateStack ()
2281 Push (DtdInputState.Free);
2284 public DtdInputState Peek ()
2286 return (DtdInputState) intern.Peek ();
2289 public DtdInputState Pop ()
2291 return (DtdInputState) intern.Pop ();
2294 public void Push (DtdInputState val)
2301 DtdInputStateStack stateStack = new DtdInputStateStack ();
2302 DtdInputState State {
2303 get { return stateStack.Peek (); }
2306 // Simply read but not generate any result.
2307 private void ReadInternalSubset ()
2309 bool continueParse = true;
2311 while (continueParse) {
2312 switch (ReadChar ()) {
2315 case DtdInputState.Free:
2316 continueParse = false;
2318 case DtdInputState.InsideDoubleQuoted:
2320 case DtdInputState.InsideSingleQuoted:
2323 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2327 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2329 if (State == DtdInputState.InsideDoubleQuoted ||
2330 State == DtdInputState.InsideSingleQuoted)
2331 continue; // well-formed
2332 switch (ReadChar ()) {
2334 stateStack.Push (DtdInputState.PI);
2337 switch (ReadChar ()) {
2339 switch (ReadChar ()) {
2342 stateStack.Push (DtdInputState.ElementDecl);
2346 stateStack.Push (DtdInputState.EntityDecl);
2349 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2354 stateStack.Push (DtdInputState.AttlistDecl);
2358 stateStack.Push (DtdInputState.NotationDecl);
2362 stateStack.Push (DtdInputState.Comment);
2367 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2371 if (State == DtdInputState.InsideSingleQuoted)
2373 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2374 stateStack.Push (DtdInputState.InsideSingleQuoted);
2377 if (State == DtdInputState.InsideDoubleQuoted)
2379 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2380 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2384 case DtdInputState.ElementDecl:
2385 goto case DtdInputState.NotationDecl;
2386 case DtdInputState.AttlistDecl:
2387 goto case DtdInputState.NotationDecl;
2388 case DtdInputState.EntityDecl:
2389 goto case DtdInputState.NotationDecl;
2390 case DtdInputState.NotationDecl:
2393 case DtdInputState.InsideDoubleQuoted:
2395 case DtdInputState.InsideSingleQuoted:
2396 continue; // well-formed
2397 case DtdInputState.Comment:
2400 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2404 if (State == DtdInputState.PI) {
2405 if (ReadChar () == '>')
2410 if (State == DtdInputState.Comment) {
2411 if (PeekChar () == '-') {
2419 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2420 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2426 // The reader is positioned on the first 'S' of "SYSTEM".
2427 private string ReadSystemLiteral (bool expectSYSTEM)
2431 if (!SkipWhitespace ())
2432 throw new XmlException (this as IXmlLineInfo,
2433 "Whitespace is required after 'SYSTEM'.");
2437 int quoteChar = ReadChar (); // apos or quot
2438 int startPos = currentTag.Length;
2440 ClearValueBuffer ();
2441 while (c != quoteChar) {
2444 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2446 AppendValueChar (c);
2448 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2451 private string ReadPubidLiteral()
2454 if (!SkipWhitespace ())
2455 throw new XmlException (this as IXmlLineInfo,
2456 "Whitespace is required after 'PUBLIC'.");
2457 int quoteChar = ReadChar ();
2458 int startPos = currentTag.Length;
2460 ClearValueBuffer ();
2461 while(c != quoteChar)
2464 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2465 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2466 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2468 AppendValueChar (c);
2470 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2473 // The reader is positioned on the first character
2475 private string ReadName ()
2477 return ReadNameOrNmToken(false);
2480 // The reader is positioned on the first character
2482 private string ReadNmToken ()
2484 return ReadNameOrNmToken(true);
2487 private string ReadNameOrNmToken (bool isNameToken)
2489 int ch = PeekChar ();
2491 if (!XmlChar.IsNameChar (ch))
2492 throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char) ch));
2495 if (!XmlChar.IsFirstNameChar (ch))
2496 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2501 AppendNameChar (ReadChar ());
2503 while (XmlChar.IsNameChar (PeekChar ())) {
2504 AppendNameChar (ReadChar ());
2507 return CreateNameString ();
2510 // Read the next character and compare it against the
2511 // specified character.
2512 private void Expect (int expected)
2514 int ch = ReadChar ();
2516 if (ch != expected) {
2517 throw new XmlException (this as IXmlLineInfo,
2519 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2527 private void Expect (string expected)
2529 int len = expected.Length;
2530 for(int i=0; i< len; i++)
2531 Expect (expected[i]);
2534 private void ExpectAfterWhitespace (char c)
2537 int i = ReadChar ();
2538 if (XmlChar.IsWhitespace (i))
2541 throw new XmlException (String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2546 // Does not consume the first non-whitespace character.
2547 private bool SkipWhitespace ()
2549 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2550 while (XmlChar.IsWhitespace (PeekChar ()))
2555 private void ReadWhitespace ()
2557 if (currentState == XmlNodeType.None)
2558 currentState = XmlNodeType.XmlDeclaration;
2560 ClearValueBuffer ();
2561 int ch = PeekChar ();
2563 AppendValueChar (ReadChar ());
2564 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2566 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2569 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2570 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2571 SetProperties (nodeType,
2578 return; // (PeekChar () != -1);
2581 private byte GetBase64Byte (char ch)
2591 if (ch >= 'A' && ch <= 'Z')
2592 return (byte) (ch - 'A');
2593 else if (ch >= 'a' && ch <= 'z')
2594 return (byte) (ch - 'a' + 26);
2595 else if (ch >= '0' && ch <= '9')
2596 return (byte) (ch - '0' + 52);
2598 throw new XmlException ("Invalid Base64 character was found.");
2602 // Returns -1 if it should throw an error.
2603 private int ReadCharsInternal (char [] buffer, int offset, int length)
2605 shouldSkipUntilEndTag = true;
2608 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2609 else if (length < 0)
2610 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2611 else if (buffer.Length < offset + length)
2612 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2614 if (NodeType != XmlNodeType.Element)
2617 int bufIndex = offset;
2618 for (int i = 0; i < length; i++) {
2619 int c = PeekChar ();
2622 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2625 if (PeekChar () != '/') {
2626 buffer [bufIndex++] = '<';
2629 // Seems to skip immediate EndElement
2631 string name = ReadName ();
2632 if (name != (string) this.elementStack.Peek ()) {
2633 if (i + 1 < length) {
2634 buffer [bufIndex++] = '<';
2635 buffer [bufIndex++] = '/';
2637 for (int n = 0; n < name.Length && i + n + 1 < length; n++)
2638 buffer [bufIndex++] = name [n];
2643 this.elementStack.Pop ();
2644 shouldSkipUntilEndTag = false;
2649 if (c < Char.MaxValue)
2650 buffer [bufIndex++] = (char) c;
2652 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800);
2653 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2661 private bool ReadUntilEndTag ()
2668 throw new XmlException (this as IXmlLineInfo,
2669 "Unexpected end of xml.");
2671 if (PeekChar () != '/')
2674 string name = ReadName ();
2675 if (name != (string) this.elementStack.Peek ())
2679 this.elementStack.Pop ();