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 ((char) peek_char);
393 return new StringReader (((char) 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 internal DTDObjectModel DTD {
778 get { return parserContext.Dtd; }
781 internal XmlResolver Resolver {
782 get { return resolver; }
787 internal class XmlTokenInfo
789 public XmlTokenInfo (XmlTextReader xtr)
797 protected XmlTextReader Reader;
800 public string LocalName;
801 public string Prefix;
802 public string NamespaceURI;
803 public bool IsEmptyElement;
804 public char QuoteChar;
805 public int LineNumber;
806 public int LinePosition;
808 public XmlNodeType NodeType;
810 public virtual string Value {
812 if (valueCache != null)
814 else if (Reader.valueBuilderAvailable) {
815 valueCache = Reader.valueBuilder.ToString ();
825 public virtual void Clear ()
828 NodeType = XmlNodeType.None;
829 Name = LocalName = Prefix = NamespaceURI = String.Empty;
830 IsEmptyElement = false;
832 LineNumber = LinePosition = 0;
835 internal virtual void FillNames ()
837 if (Reader.Namespaces) {
838 int indexOfColon = Name.IndexOf (':');
840 if (indexOfColon == -1) {
841 Prefix = String.Empty;
844 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
845 // However, this might be reverted if NameTable is got improved.
846 char [] nameArr = Name.ToCharArray ();
847 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
848 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
849 // Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
850 // LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
855 case XmlNodeType.Attribute:
856 if (Prefix == string.Empty)
857 NamespaceURI = string.Empty;
859 NamespaceURI = Reader.LookupNamespace (Prefix, true);
862 case XmlNodeType.Element:
863 case XmlNodeType.EndElement:
864 NamespaceURI = Reader.LookupNamespace (Prefix, true);
871 Prefix = String.Empty;
877 internal class XmlAttributeTokenInfo : XmlTokenInfo
879 public XmlAttributeTokenInfo (XmlTextReader reader)
882 NodeType = XmlNodeType.Attribute;
885 public int ValueTokenStartIndex;
886 public int ValueTokenEndIndex;
888 bool cachedNormalization;
889 StringBuilder tmpBuilder = new StringBuilder ();
891 public override string Value {
893 if (cachedNormalization != Reader.Normalization)
895 if (valueCache != null)
897 cachedNormalization = Reader.Normalization;
899 // An empty value should return String.Empty.
900 if (ValueTokenStartIndex == ValueTokenEndIndex) {
901 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
902 if (ti.NodeType == XmlNodeType.EntityReference)
903 valueCache = String.Concat ("&", ti.Name, ";");
905 valueCache = ti.Value;
906 if (cachedNormalization)
911 tmpBuilder.Length = 0;
912 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
913 XmlTokenInfo ti = Reader.attributeValueTokens [i];
914 if (ti.NodeType == XmlNodeType.Text)
915 tmpBuilder.Append (ti.Value);
917 tmpBuilder.Append ('&');
918 tmpBuilder.Append (ti.Name);
919 tmpBuilder.Append (';');
923 valueCache = tmpBuilder.ToString ();
924 if (cachedNormalization)
933 public override void Clear ()
937 NodeType = XmlNodeType.Attribute;
938 ValueTokenStartIndex = ValueTokenEndIndex = 0;
941 internal override void FillNames ()
944 if (Prefix == "xmlns" || Name == "xmlns")
945 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
948 private void NormalizeSpaces ()
950 tmpBuilder.Length = 0;
951 for (int i = 0; i < valueCache.Length; i++)
952 switch (valueCache [i]) {
954 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
959 tmpBuilder.Append (' ');
962 tmpBuilder.Append (valueCache [i]);
965 valueCache = tmpBuilder.ToString ();
969 private XmlTokenInfo cursorToken;
970 private XmlTokenInfo currentToken;
971 private XmlAttributeTokenInfo currentAttributeToken;
972 private XmlTokenInfo currentAttributeValueToken;
973 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
974 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
975 private int currentAttribute;
976 private int currentAttributeValue;
977 private int attributeCount;
979 private XmlParserContext parserContext;
981 private ReadState readState;
984 private int elementDepth;
985 private bool depthUp;
987 private bool popScope;
988 private Stack elementStack = new Stack();
989 private bool allowMultipleRoot;
991 private bool isStandalone;
993 private StringBuilder valueBuilder;
994 private bool valueBuilderAvailable = false;
996 private bool returnEntityReference;
997 private string entityReferenceName;
999 private char [] nameBuffer;
1000 private int nameLength;
1001 private int nameCapacity;
1002 private const int initialNameCapacity = 256;
1004 private StringBuilder valueBuffer = new StringBuilder (512);
1006 private TextReader reader;
1007 private bool can_seek;
1008 private bool has_peek;
1009 private int peek_char;
1010 private bool hasPeekChars;
1011 private char [] peekChars;
1012 private int peekCharsIndex;
1016 private StringBuilder currentTag = new StringBuilder ();
1018 private int currentLinkedNodeLineNumber;
1019 private int currentLinkedNodeLinePosition;
1020 private bool useProceedingLineInfo;
1022 // A buffer for ReadContent for ReadOuterXml
1024 private XmlNodeType startNodeType;
1025 // State machine attribute.
1026 // XmlDeclaration: after the first node.
1027 // DocumentType: after doctypedecl
1028 // Element: inside document element
1029 // EndElement: after document element
1030 private XmlNodeType currentState;
1032 // For ReadChars()/ReadBase64()/ReadBinHex()
1033 private bool shouldSkipUntilEndTag;
1034 private byte [] base64Cache = new byte [3];
1035 private int base64CacheStartsAt;
1037 // These values are never re-initialized.
1038 private bool namespaces = true;
1039 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
1040 private XmlResolver resolver = new XmlUrlResolver ();
1041 private bool normalization = false;
1043 private void Init ()
1045 currentToken = new XmlTokenInfo (this);
1046 cursorToken = currentToken;
1047 currentAttribute = -1;
1048 currentAttributeValue = -1;
1051 readState = ReadState.Initial;
1052 allowMultipleRoot = false;
1058 popScope = allowMultipleRoot = false;
1059 elementStack.Clear ();
1061 isStandalone = false;
1062 valueBuilderAvailable = false;
1063 returnEntityReference = false;
1064 entityReferenceName = String.Empty;
1066 nameBuffer = new char [initialNameCapacity];
1068 nameCapacity = initialNameCapacity;
1070 can_seek = has_peek = false;
1071 peek_char = peekCharsIndex = 0;
1072 peekChars = new char [6];
1076 currentTag.Length = 0;
1078 valueBuffer.Length = 0;
1080 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
1081 useProceedingLineInfo = false;
1083 currentState = XmlNodeType.None;
1085 shouldSkipUntilEndTag = false;
1086 base64CacheStartsAt = -1;
1089 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1091 startNodeType = fragType;
1092 parserContext = context;
1093 if (context == null) {
1094 XmlNameTable nt = new NameTable ();
1095 parserContext = new XmlParserContext (nt,
1096 new XmlNamespaceManager (nt),
1101 if (url != null && url.Length > 0) {
1104 uri = new Uri (url);
1105 } catch (Exception) {
1106 string path = Path.GetFullPath ("./a");
1107 uri = new Uri (new Uri (path), url);
1109 parserContext.BaseURI = uri.ToString ();
1115 case XmlNodeType.Attribute:
1116 fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1118 case XmlNodeType.Element:
1119 currentState = XmlNodeType.Element;
1120 allowMultipleRoot = true;
1122 case XmlNodeType.Document:
1125 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1131 // Use this method rather than setting the properties
1132 // directly so that all the necessary properties can
1133 // be changed in harmony with each other. Maybe the
1134 // fields should be in a seperate class to help enforce
1136 private void SetProperties (
1137 XmlNodeType nodeType,
1139 bool isEmptyElement,
1141 bool clearAttributes)
1143 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1144 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1145 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1148 private void SetProperties (
1150 XmlNodeType nodeType,
1152 bool isEmptyElement,
1154 bool clearAttributes)
1156 this.valueBuilderAvailable = false;
1158 token.NodeType = nodeType;
1160 token.IsEmptyElement = isEmptyElement;
1161 token.Value = value;
1162 this.elementDepth = depth;
1164 if (clearAttributes)
1170 private void SetProperties (
1171 XmlNodeType nodeType,
1173 bool isEmptyElement,
1174 bool clearAttributes,
1175 StringBuilder value) {
1176 SetProperties (nodeType, name, isEmptyElement, (string)null, clearAttributes);
1177 this.valueBuilderAvailable = true;
1178 this.valueBuilder = value;
1181 private void ClearAttributes ()
1183 for (int i = 0; i < attributeCount; i++)
1184 attributeTokens [i].Clear ();
1186 currentAttribute = -1;
1187 currentAttributeValue = -1;
1190 private int PeekChar ()
1193 return reader.Peek ();
1196 return peekChars [peekCharsIndex];
1201 peek_char = reader.Read ();
1206 private int ReadChar ()
1211 ch = peekChars [peekCharsIndex++];
1212 if (peekChars [peekCharsIndex] == 0)
1213 hasPeekChars = false;
1214 } else if (has_peek) {
1218 ch = reader.Read ();
1227 currentTag.Append ((char) ch);
1231 // This should really keep track of some state so
1232 // that it's not possible to have more than one document
1233 // element or text outside of the document element.
1234 private bool ReadContent ()
1236 currentTag.Length = 0;
1238 parserContext.NamespaceManager.PopScope ();
1242 if (returnEntityReference)
1243 SetEntityReferenceProperties ();
1245 switch (PeekChar ()) {
1250 case '\r': goto case ' ';
1251 case '\n': goto case ' ';
1252 case '\t': goto case ' ';
1254 if (whitespaceHandling == WhitespaceHandling.All ||
1255 whitespaceHandling == WhitespaceHandling.Significant)
1259 return ReadContent ();
1263 readState = ReadState.EndOfFile;
1265 XmlNodeType.None, // nodeType
1266 String.Empty, // name
1267 false, // isEmptyElement
1268 (string) null, // value
1269 true // clearAttributes
1272 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1280 return this.ReadState != ReadState.EndOfFile;
1283 private void SetEntityReferenceProperties ()
1285 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1286 // if (DTD != null && resolver != null && decl == null)
1287 // throw new XmlException (this as IXmlLineInfo, "Entity declaration does not exist.");
1288 if (this.isStandalone)
1289 if (DTD == null || decl == null || !decl.IsInternalSubset)
1290 throw new XmlException (this as IXmlLineInfo,
1291 "Standalone document must not contain any references to an non-internally declared entity.");
1292 if (decl != null && decl.NotationName != null)
1293 throw new XmlException (this as IXmlLineInfo,
1294 "Reference to any unparsed entities is not allowed here.");
1297 XmlNodeType.EntityReference, // nodeType
1298 entityReferenceName, // name
1299 false, // isEmptyElement
1300 (string) null, // value
1301 true // clearAttributes
1304 returnEntityReference = false;
1305 entityReferenceName = String.Empty;
1308 // The leading '<' has already been consumed.
1309 private void ReadTag ()
1311 switch (PeekChar ())
1319 ReadProcessingInstruction ();
1331 // The leading '<' has already been consumed.
1332 private void ReadStartTag ()
1334 if (currentState == XmlNodeType.EndElement)
1335 throw new XmlException (this as IXmlLineInfo,
1336 "Element cannot appear in this state.");
1337 currentState = XmlNodeType.Element;
1339 parserContext.NamespaceManager.PushScope ();
1341 string name = ReadName ();
1342 if (currentState == XmlNodeType.EndElement)
1343 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1345 bool isEmptyElement = false;
1350 if (XmlChar.IsFirstNameChar (PeekChar ()))
1351 ReadAttributes (false);
1352 cursorToken = this.currentToken;
1355 for (int i = 0; i < attributeCount; i++)
1356 attributeTokens [i].FillNames ();
1359 for (int i = 0; i < attributeCount; i++)
1360 for (int j = i + 1; j < attributeCount; j++)
1361 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1362 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1363 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1364 throw new XmlException (this as IXmlLineInfo,
1365 "Attribute name and qualified name must be identical.");
1367 string baseUri = GetAttribute ("xml:base");
1368 if (baseUri != null)
1369 parserContext.BaseURI = baseUri;
1370 string xmlLang = GetAttribute ("xml:lang");
1371 if (xmlLang != null)
1372 parserContext.XmlLang = xmlLang;
1373 string xmlSpaceAttr = GetAttribute ("xml:space");
1374 if (xmlSpaceAttr != null) {
1375 if (xmlSpaceAttr == "preserve")
1376 parserContext.XmlSpace = XmlSpace.Preserve;
1377 else if (xmlSpaceAttr == "default")
1378 parserContext.XmlSpace = XmlSpace.Default;
1380 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1382 if (PeekChar () == '/') {
1384 isEmptyElement = true;
1389 elementStack.Push (name);
1390 parserContext.PushScope ();
1396 XmlNodeType.Element, // nodeType
1398 isEmptyElement, // isEmptyElement
1399 (string) null, // value
1400 false // clearAttributes
1404 // The reader is positioned on the first character
1405 // of the element's name.
1406 private void ReadEndTag ()
1408 if (currentState != XmlNodeType.Element)
1409 throw new XmlException (this as IXmlLineInfo,
1410 "End tag cannot appear in this state.");
1412 string name = ReadName ();
1413 if (elementStack.Count == 0)
1414 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1415 string expected = (string)elementStack.Pop();
1416 if (expected != name)
1417 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1418 parserContext.PopScope ();
1420 ExpectAfterWhitespace ('>');
1425 XmlNodeType.EndElement, // nodeType
1427 false, // isEmptyElement
1428 (string) null, // value
1429 true // clearAttributes
1435 private void AppendNameChar (int ch)
1437 CheckNameCapacity ();
1438 nameBuffer [nameLength++] = (char)ch;
1441 private void CheckNameCapacity ()
1443 if (nameLength == nameCapacity) {
1444 nameCapacity = nameCapacity * 2;
1445 char [] oldNameBuffer = nameBuffer;
1446 nameBuffer = new char [nameCapacity];
1447 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1451 private string CreateNameString ()
1453 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1456 private void AppendValueChar (int ch)
1458 valueBuffer.Append ((char)ch);
1461 private string CreateValueString ()
1463 return valueBuffer.ToString ();
1466 private void ClearValueBuffer ()
1468 valueBuffer.Length = 0;
1471 // The reader is positioned on the first character
1473 private void ReadText (bool notWhitespace)
1475 if (currentState != XmlNodeType.Element)
1476 throw new XmlException (this as IXmlLineInfo,
1477 "Text node cannot appear in this state.");
1480 ClearValueBuffer ();
1482 int ch = PeekChar ();
1483 int previousCloseBracketLine = 0;
1484 int previousCloseBracketColumn = 0;
1486 while (ch != '<' && ch != -1) {
1489 ch = ReadReference (false);
1490 if (returnEntityReference) // Returns -1 if char validation should not be done
1496 if (normalization && XmlConstructs.IsInvalid (ch))
1497 throw new XmlException (this as IXmlLineInfo,
1498 "Not allowed character was found.");
1499 AppendValueChar (ch);
1503 if (previousCloseBracketColumn == LinePosition - 1 &&
1504 previousCloseBracketLine == LineNumber)
1505 if (PeekChar () == '>')
1506 throw new XmlException (this as IXmlLineInfo,
1507 "Inside text content, character sequence ']]>' is not allowed.");
1508 // This tricky style is required to check "] ]]>"
1509 previousCloseBracketColumn = LinePosition;
1510 previousCloseBracketLine = LineNumber;
1513 notWhitespace = true;
1516 if (returnEntityReference && valueBuffer.Length == 0) {
1517 SetEntityReferenceProperties ();
1519 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1520 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1522 nodeType, // nodeType
1523 String.Empty, // name
1524 false, // isEmptyElement
1525 true, // clearAttributes
1526 valueBuffer // value
1531 // The leading '&' has already been consumed.
1532 // Returns true if the entity reference isn't a simple
1533 // character reference or one of the predefined entities.
1534 // This allows the ReadText method to break so that the
1535 // next call to Read will return the EntityReference node.
1536 private int ReadReference (bool ignoreEntityReferences)
1538 if (PeekChar () == '#') {
1540 return ReadCharacterReference ();
1542 return ReadEntityReference (ignoreEntityReferences);
1545 private int ReadCharacterReference ()
1549 if (PeekChar () == 'x') {
1552 while (PeekChar () != ';' && PeekChar () != -1) {
1553 int ch = ReadChar ();
1555 if (ch >= '0' && ch <= '9')
1556 value = (value << 4) + ch - '0';
1557 else if (ch >= 'A' && ch <= 'F')
1558 value = (value << 4) + ch - 'A' + 10;
1559 else if (ch >= 'a' && ch <= 'f')
1560 value = (value << 4) + ch - 'a' + 10;
1562 throw new XmlException (this as IXmlLineInfo,
1564 "invalid hexadecimal digit: {0} (#x{1:X})",
1569 while (PeekChar () != ';' && PeekChar () != -1) {
1570 int ch = ReadChar ();
1572 if (ch >= '0' && ch <= '9')
1573 value = value * 10 + ch - '0';
1575 throw new XmlException (this as IXmlLineInfo,
1577 "invalid decimal digit: {0} (#x{1:X})",
1585 // There is no way to save surrogate pairs...
1586 if (normalization && value < 0xffff && !XmlConstructs.IsValid (value))
1587 throw new XmlException (this as IXmlLineInfo,
1588 "Referenced character was not allowed in XML.");
1592 // Returns -1 if it should not be validated.
1593 // Real EOF must not be detected here.
1594 private int ReadEntityReference (bool ignoreEntityReferences)
1598 int ch = PeekChar ();
1600 while (ch != ';' && ch != -1) {
1601 AppendNameChar (ReadChar ());
1607 string name = CreateNameString ();
1608 if (!XmlChar.IsName (name))
1609 throw new XmlException (this as IXmlLineInfo,
1610 "Invalid entity reference name was found.");
1612 char predefined = XmlChar.GetPredefinedEntity (name);
1613 if (predefined != 0)
1614 // AppendValueChar (predefined);
1617 if (ignoreEntityReferences) {
1618 AppendValueChar ('&');
1620 foreach (char ch2 in name) {
1621 AppendValueChar (ch2);
1624 AppendValueChar (';');
1626 returnEntityReference = true;
1627 entityReferenceName = name;
1633 // The reader is positioned on the first character of
1634 // the attribute name.
1635 private void ReadAttributes (bool endsWithQuestion)
1638 bool requireWhitespace = false;
1639 currentAttribute = -1;
1640 currentAttributeValue = -1;
1643 if (!SkipWhitespace () && requireWhitespace)
1644 throw new XmlException ("Unexpected token. Name is required here.");
1646 IncrementAttributeToken ();
1647 currentAttributeToken.LineNumber = line;
1648 currentAttributeToken.LinePosition = column;
1650 currentAttributeToken.Name = ReadName ();
1651 ExpectAfterWhitespace ('=');
1653 ReadAttributeValueTokens (-1);
1656 if (currentAttributeToken.Name == "xmlns")
1657 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1658 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1659 string nsPrefix = currentAttributeToken.Name.Substring (6);
1660 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1663 if (!SkipWhitespace ())
1664 requireWhitespace = true;
1665 peekChar = PeekChar ();
1666 if (endsWithQuestion) {
1667 if (peekChar == '?')
1670 else if (peekChar == '/' || peekChar == '>')
1672 } while (peekChar != -1);
1674 currentAttribute = -1;
1675 currentAttributeValue = -1;
1678 private void AddAttribute (string name, string value)
1680 IncrementAttributeToken ();
1681 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1682 ati.Name = "SYSTEM";
1684 IncrementAttributeValueToken ();
1685 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1687 SetProperties (vti, XmlNodeType.Text, name, false, value, false);
1691 private void IncrementAttributeToken ()
1694 if (attributeTokens.Length == currentAttribute) {
1695 XmlAttributeTokenInfo [] newArray =
1696 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1697 attributeTokens.CopyTo (newArray, 0);
1698 attributeTokens = newArray;
1700 if (attributeTokens [currentAttribute] == null)
1701 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1702 currentAttributeToken = attributeTokens [currentAttribute];
1703 currentAttributeToken.Clear ();
1706 private void IncrementAttributeValueToken ()
1708 ClearValueBuffer ();
1709 currentAttributeValue++;
1710 if (attributeValueTokens.Length == currentAttributeValue) {
1711 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1712 attributeValueTokens.CopyTo (newArray, 0);
1713 attributeValueTokens = newArray;
1715 if (attributeValueTokens [currentAttributeValue] == null)
1716 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this);
1717 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1718 currentAttributeValueToken.Clear ();
1721 private void ReadAttributeValueTokens (int dummyQuoteChar)
1723 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1725 if (quoteChar != '\'' && quoteChar != '\"')
1726 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1727 currentAttributeToken.QuoteChar = (char) quoteChar;
1729 IncrementAttributeValueToken ();
1730 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1731 currentAttributeValueToken.LineNumber = line;
1732 currentAttributeValueToken.LinePosition = column;
1734 bool incrementToken = false;
1735 bool isNewToken = true;
1740 if (ch == quoteChar)
1743 if (incrementToken) {
1744 IncrementAttributeValueToken ();
1745 currentAttributeValueToken.LineNumber = line;
1746 currentAttributeValueToken.LinePosition = column;
1747 incrementToken = false;
1754 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1756 if (dummyQuoteChar < 0)
1757 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1758 else // Attribute value constructor.
1762 int startPosition = currentTag.Length - 1;
1763 if (PeekChar () == '#') {
1765 ch = ReadCharacterReference ();
1766 AppendValueChar (ch);
1769 // Check XML 1.0 section 3.1 WFC.
1770 string entName = ReadName ();
1772 int predefined = XmlChar.GetPredefinedEntity (entName);
1773 if (predefined == 0) {
1774 DTDEntityDeclaration entDecl =
1775 DTD == null ? null : DTD.EntityDecls [entName];
1776 if (DTD != null && resolver != null && entDecl == null)
1777 throw new XmlException (this as IXmlLineInfo, "Entity declaration does not exist.");
1778 if (entDecl != null && entDecl.HasExternalReference)
1779 throw new XmlException (this as IXmlLineInfo,
1780 "Reference to external entities is not allowed in the value of an attribute.");
1781 if (isStandalone && !entDecl.IsInternalSubset)
1782 throw new XmlException (this as IXmlLineInfo,
1783 "Reference to external entities is not allowed in the value of an attribute.");
1784 if (entDecl != null && entDecl.EntityValue.IndexOf ('<') >= 0)
1785 throw new XmlException (this as IXmlLineInfo,
1786 "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1787 currentAttributeValueToken.Value = CreateValueString ();
1788 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1790 IncrementAttributeValueToken ();
1791 currentAttributeValueToken.Name = entName;
1792 currentAttributeValueToken.Value = String.Empty;
1793 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1794 incrementToken = true;
1797 AppendValueChar (predefined);
1800 AppendValueChar (ch);
1806 if (!incrementToken) {
1807 currentAttributeValueToken.Value = CreateValueString ();
1808 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1810 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1814 // The reader is positioned on the first character
1817 // It may be xml declaration or processing instruction.
1818 private void ReadProcessingInstruction ()
1820 string target = ReadName ();
1821 if (target == "xml") {
1822 ReadXmlDeclaration ();
1824 } else if (target.ToLower () == "xml")
1825 throw new XmlException (this as IXmlLineInfo,
1826 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1828 if (currentState == XmlNodeType.None)
1829 currentState = XmlNodeType.XmlDeclaration;
1831 if (!SkipWhitespace ())
1832 if (PeekChar () != '?')
1833 throw new XmlException (this as IXmlLineInfo,
1834 "Invalid processing instruction name was found.");
1836 ClearValueBuffer ();
1838 while (PeekChar () != -1) {
1839 int ch = ReadChar ();
1841 if (ch == '?' && PeekChar () == '>') {
1846 AppendValueChar ((char)ch);
1850 XmlNodeType.ProcessingInstruction, // nodeType
1852 false, // isEmptyElement
1853 true, // clearAttributes
1854 valueBuffer // value
1858 // The reader is positioned after "<?xml "
1859 private void ReadXmlDeclaration ()
1861 if (currentState != XmlNodeType.None) {
1862 throw new XmlException (this as IXmlLineInfo,
1863 "XML declaration cannot appear in this state.");
1865 currentState = XmlNodeType.XmlDeclaration;
1869 ReadAttributes (true); // They must have "version."
1870 string version = GetAttribute ("version");
1872 string message = null;
1874 if (attributeTokens [0].Name != "version" || version != "1.0")
1875 message = "Version 1.0 declaration is required in XML Declaration.";
1876 else if (attributeCount > 1 &&
1877 (attributeTokens [1].Name != "encoding" &&
1878 attributeTokens [1].Name != "standalone"))
1879 message = "Invalid Xml Declaration markup was found.";
1880 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1881 message = "Invalid Xml Declaration markup was found.";
1882 string sa = GetAttribute ("standalone");
1883 if (sa != null && sa != "yes" && sa != "no")
1884 message = "Only 'yes' or 'no' is allowed for standalone.";
1886 this.isStandalone = (sa == "yes");
1888 if (message != null)
1889 throw new XmlException (this as IXmlLineInfo, message);
1892 XmlNodeType.XmlDeclaration, // nodeType
1894 false, // isEmptyElement
1895 currentTag.ToString (6, currentTag.Length - 6), // value
1896 false // clearAttributes
1902 internal void SkipTextDeclaration ()
1904 this.currentState = XmlNodeType.Element;
1906 if (PeekChar () != '<')
1910 peekChars [0] = '<';
1912 if (PeekChar () != '?') {
1913 hasPeekChars = true;
1917 peekChars [1] = '?';
1919 for (int i = 2; i < 6; i++) {
1920 if (PeekChar () == 0)
1923 peekChars [i] = (char) ReadChar ();
1925 if (new string (peekChars, 2, 4) != "xml ") {
1926 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1927 throw new XmlException (this as IXmlLineInfo,
1928 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1930 hasPeekChars = true;
1934 for (int i = 0; i < 6; i++)
1935 peekChars [i] = '\0';
1940 if (PeekChar () == 'v') {
1942 ExpectAfterWhitespace ('=');
1944 int quoteChar = ReadChar ();
1945 char [] expect1_0 = new char [3];
1946 int versionLength = 0;
1947 switch (quoteChar) {
1950 while (PeekChar () != quoteChar) {
1951 if (PeekChar () == -1)
1952 throw new XmlException (this as IXmlLineInfo,
1953 "Invalid version declaration inside text declaration.");
1954 else if (versionLength == 3)
1955 throw new XmlException (this as IXmlLineInfo,
1956 "Invalid version number inside text declaration.");
1958 expect1_0 [versionLength] = (char) ReadChar ();
1960 if (versionLength == 3 && new String (expect1_0) != "1.0")
1961 throw new XmlException (this as IXmlLineInfo,
1962 "Invalid version number inside text declaration.");
1969 throw new XmlException (this as IXmlLineInfo,
1970 "Invalid version declaration inside text declaration.");
1974 if (PeekChar () == 'e') {
1975 Expect ("encoding");
1976 ExpectAfterWhitespace ('=');
1978 int quoteChar = ReadChar ();
1979 switch (quoteChar) {
1982 while (PeekChar () != quoteChar)
1983 if (ReadChar () == -1)
1984 throw new XmlException (this as IXmlLineInfo,
1985 "Invalid encoding declaration inside text declaration.");
1990 throw new XmlException (this as IXmlLineInfo,
1991 "Invalid encoding declaration inside text declaration.");
1993 // Encoding value should be checked inside XmlInputStream.
1996 throw new XmlException (this as IXmlLineInfo,
1997 "Encoding declaration is mandatory in text declaration.");
2002 // The reader is positioned on the first character after
2003 // the leading '<!'.
2004 private void ReadDeclaration ()
2006 int ch = PeekChar ();
2024 throw new XmlException (this as IXmlLineInfo,
2025 "Unexpected declaration markup was found.");
2029 // The reader is positioned on the first character after
2030 // the leading '<!--'.
2031 private void ReadComment ()
2033 if (currentState == XmlNodeType.None)
2034 currentState = XmlNodeType.XmlDeclaration;
2036 ClearValueBuffer ();
2038 while (PeekChar () != -1) {
2039 int ch = ReadChar ();
2041 if (ch == '-' && PeekChar () == '-') {
2044 if (PeekChar () != '>')
2045 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2051 if (XmlConstructs.IsInvalid (ch))
2052 throw new XmlException (this as IXmlLineInfo,
2053 "Not allowed character was found.");
2055 AppendValueChar ((char)ch);
2059 XmlNodeType.Comment, // nodeType
2060 String.Empty, // name
2061 false, // isEmptyElement
2062 true, // clearAttributes
2063 valueBuffer // value
2067 // The reader is positioned on the first character after
2068 // the leading '<![CDATA['.
2069 private void ReadCDATA ()
2071 if (currentState != XmlNodeType.Element)
2072 throw new XmlException (this as IXmlLineInfo,
2073 "CDATA section cannot appear in this state.");
2075 ClearValueBuffer ();
2079 while (PeekChar () != -1) {
2084 if (ch == ']' && PeekChar () == ']') {
2085 ch = ReadChar (); // ']'
2087 if (PeekChar () == '>') {
2092 // AppendValueChar (']');
2093 // AppendValueChar (']');
2094 // ch = ReadChar ();
2098 AppendValueChar ((char)ch);
2102 XmlNodeType.CDATA, // 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 '<!DOCTYPE'.
2112 private void ReadDoctypeDecl ()
2114 switch (currentState) {
2115 case XmlNodeType.DocumentType:
2116 case XmlNodeType.Element:
2117 case XmlNodeType.EndElement:
2118 throw new XmlException (this as IXmlLineInfo,
2119 "Document type cannot appear in this state.");
2121 currentState = XmlNodeType.DocumentType;
2123 string doctypeName = null;
2124 string publicId = null;
2125 string systemId = null;
2126 int intSubsetStartLine = 0;
2127 int intSubsetStartColumn = 0;
2130 doctypeName = ReadName ();
2135 systemId = ReadSystemLiteral (true);
2138 publicId = ReadPubidLiteral ();
2139 if (!SkipWhitespace ())
2140 throw new XmlException (this as IXmlLineInfo,
2141 "Whitespace is required between PUBLIC id and SYSTEM id.");
2142 systemId = ReadSystemLiteral (false);
2148 if(PeekChar () == '[')
2150 // read markupdecl etc. or end of decl
2152 intSubsetStartLine = this.LineNumber;
2153 intSubsetStartColumn = this.LinePosition;
2154 int startPos = currentTag.Length;
2155 ReadInternalSubset ();
2156 int endPos = currentTag.Length - 1;
2157 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
2159 // end of DOCTYPE decl.
2160 ExpectAfterWhitespace ('>');
2162 GenerateDTDObjectModel (doctypeName, publicId,
2163 systemId, parserContext.InternalSubset,
2164 intSubsetStartLine, intSubsetStartColumn);
2166 // set properties for <!DOCTYPE> node
2168 XmlNodeType.DocumentType, // nodeType
2169 doctypeName, // name
2170 false, // isEmptyElement
2171 parserContext.InternalSubset, // value
2172 true // clearAttributes
2175 if (publicId != null)
2176 AddAttribute ("PUBLIC", publicId);
2177 if (systemId != null)
2178 AddAttribute ("SYSTEM", systemId);
2181 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2182 string systemId, string internalSubset)
2184 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2187 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2188 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2191 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2192 DTD.BaseURI = BaseURI;
2194 DTD.PublicId = publicId;
2195 DTD.SystemId = systemId;
2196 DTD.InternalSubset = internalSubset;
2197 DTD.XmlResolver = resolver;
2198 DTD.IsStandalone = isStandalone;
2199 DTD.LineNumber = line;
2200 DTD.LinePosition = column;
2202 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2203 dr.Normalization = this.normalization;
2204 return dr.GenerateDTDObjectModel ();
2207 private enum DtdInputState
2220 private class DtdInputStateStack
2222 Stack intern = new Stack ();
2223 public DtdInputStateStack ()
2225 Push (DtdInputState.Free);
2228 public DtdInputState Peek ()
2230 return (DtdInputState) intern.Peek ();
2233 public DtdInputState Pop ()
2235 return (DtdInputState) intern.Pop ();
2238 public void Push (DtdInputState val)
2245 DtdInputStateStack stateStack = new DtdInputStateStack ();
2246 DtdInputState State {
2247 get { return stateStack.Peek (); }
2250 // Simply read but not generate any result.
2251 private void ReadInternalSubset ()
2253 bool continueParse = true;
2255 while (continueParse) {
2256 switch (ReadChar ()) {
2259 case DtdInputState.Free:
2260 continueParse = false;
2262 case DtdInputState.InsideDoubleQuoted:
2264 case DtdInputState.InsideSingleQuoted:
2267 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2271 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2273 if (State == DtdInputState.InsideDoubleQuoted ||
2274 State == DtdInputState.InsideSingleQuoted)
2275 continue; // well-formed
2276 switch (ReadChar ()) {
2278 stateStack.Push (DtdInputState.PI);
2281 switch (ReadChar ()) {
2283 switch (ReadChar ()) {
2286 stateStack.Push (DtdInputState.ElementDecl);
2290 stateStack.Push (DtdInputState.EntityDecl);
2293 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2298 stateStack.Push (DtdInputState.AttlistDecl);
2302 stateStack.Push (DtdInputState.NotationDecl);
2306 stateStack.Push (DtdInputState.Comment);
2311 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2315 if (State == DtdInputState.InsideSingleQuoted)
2317 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2318 stateStack.Push (DtdInputState.InsideSingleQuoted);
2321 if (State == DtdInputState.InsideDoubleQuoted)
2323 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2324 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2328 case DtdInputState.ElementDecl:
2329 goto case DtdInputState.NotationDecl;
2330 case DtdInputState.AttlistDecl:
2331 goto case DtdInputState.NotationDecl;
2332 case DtdInputState.EntityDecl:
2333 goto case DtdInputState.NotationDecl;
2334 case DtdInputState.NotationDecl:
2337 case DtdInputState.InsideDoubleQuoted:
2339 case DtdInputState.InsideSingleQuoted:
2340 continue; // well-formed
2341 case DtdInputState.Comment:
2344 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2348 if (State == DtdInputState.PI) {
2349 if (ReadChar () == '>')
2354 if (State == DtdInputState.Comment) {
2355 if (PeekChar () == '-') {
2363 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2364 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2370 // The reader is positioned on the first 'S' of "SYSTEM".
2371 private string ReadSystemLiteral (bool expectSYSTEM)
2375 if (!SkipWhitespace ())
2376 throw new XmlException (this as IXmlLineInfo,
2377 "Whitespace is required after 'SYSTEM'.");
2381 int quoteChar = ReadChar (); // apos or quot
2382 int startPos = currentTag.Length;
2384 ClearValueBuffer ();
2385 while (c != quoteChar) {
2388 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2390 AppendValueChar (c);
2392 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2395 private string ReadPubidLiteral()
2398 if (!SkipWhitespace ())
2399 throw new XmlException (this as IXmlLineInfo,
2400 "Whitespace is required after 'PUBLIC'.");
2401 int quoteChar = ReadChar ();
2402 int startPos = currentTag.Length;
2404 ClearValueBuffer ();
2405 while(c != quoteChar)
2408 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2409 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2410 throw new XmlException (this as IXmlLineInfo,"character '" + (char)c + "' not allowed for PUBLIC ID");
2412 AppendValueChar (c);
2414 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2417 // The reader is positioned on the first character
2419 private string ReadName ()
2421 return ReadNameOrNmToken(false);
2424 // The reader is positioned on the first character
2426 private string ReadNmToken ()
2428 return ReadNameOrNmToken(true);
2431 private string ReadNameOrNmToken (bool isNameToken)
2433 int ch = PeekChar ();
2435 if (!XmlChar.IsNameChar ((char) ch))
2436 throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char)ch));
2439 if (!XmlChar.IsFirstNameChar (ch))
2440 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
2445 AppendNameChar (ReadChar ());
2447 while (XmlChar.IsNameChar (PeekChar ())) {
2448 AppendNameChar (ReadChar ());
2451 return CreateNameString ();
2454 // Read the next character and compare it against the
2455 // specified character.
2456 private void Expect (int expected)
2458 int ch = ReadChar ();
2460 if (ch != expected) {
2461 throw new XmlException (this as IXmlLineInfo,
2463 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2471 private void Expect (string expected)
2473 int len = expected.Length;
2474 for(int i=0; i< len; i++)
2475 Expect (expected[i]);
2478 private void ExpectAfterWhitespace (char c)
2481 int i = ReadChar ();
2482 if (XmlChar.IsWhitespace (i))
2485 throw new XmlException (String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2490 // Does not consume the first non-whitespace character.
2491 private bool SkipWhitespace ()
2493 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2494 while (XmlChar.IsWhitespace (PeekChar ()))
2499 private void ReadWhitespace ()
2501 if (currentState == XmlNodeType.None)
2502 currentState = XmlNodeType.XmlDeclaration;
2504 ClearValueBuffer ();
2505 int ch = PeekChar ();
2507 AppendValueChar (ReadChar ());
2508 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2510 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2513 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2514 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2515 SetProperties (nodeType,
2522 return; // (PeekChar () != -1);
2525 private byte GetBase64Byte (char ch)
2535 if (ch >= 'A' && ch <= 'Z')
2536 return (byte) (ch - 'A');
2537 else if (ch >= 'a' && ch <= 'z')
2538 return (byte) (ch - 'a' + 26);
2539 else if (ch >= '0' && ch <= '9')
2540 return (byte) (ch - '0' + 52);
2542 throw new XmlException ("Invalid Base64 character was found.");
2546 // Returns -1 if it should throw an error.
2547 private int ReadCharsInternal (char [] buffer, int offset, int length)
2549 shouldSkipUntilEndTag = true;
2552 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2553 else if (length < 0)
2554 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2555 else if (buffer.Length < offset + length)
2556 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2558 if (NodeType != XmlNodeType.Element)
2561 int bufIndex = offset;
2562 for (int i = 0; i < length; i++) {
2563 int c = PeekChar ();
2566 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2569 if (PeekChar () != '/') {
2570 buffer [bufIndex++] = '<';
2573 // Seems to skip immediate EndElement
2575 string name = ReadName ();
2576 if (name != (string) this.elementStack.Peek ()) {
2577 if (i + 1 < length) {
2578 buffer [bufIndex++] = '<';
2579 buffer [bufIndex++] = '/';
2581 for (int n = 0; n < name.Length && i + n + 1 < length; n++)
2582 buffer [bufIndex++] = name [n];
2587 this.elementStack.Pop ();
2588 shouldSkipUntilEndTag = false;
2592 buffer [bufIndex++] = (char) ReadChar ();
2599 private bool ReadUntilEndTag ()
2606 throw new XmlException (this as IXmlLineInfo,
2607 "Unexpected end of xml.");
2609 if (PeekChar () != '/')
2612 string name = ReadName ();
2613 if (name != (string) this.elementStack.Peek ())
2617 this.elementStack.Pop ();