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/
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 // Some thought needs to be given to performance.
37 // If current node is on an Attribute, Prefix might be null, and
38 // in several fields which uses XmlReader, it should be considered.
42 using System.Collections;
43 using System.Globalization;
\r
45 using System.Security.Policy;
47 using System.Xml.Schema;
52 public class XmlTextReader : XmlReader, IXmlLineInfo
56 protected XmlTextReader ()
60 public XmlTextReader (Stream input)
61 : this (new XmlStreamReader (input))
65 public XmlTextReader (string url)
66 : this(url, new NameTable ())
70 public XmlTextReader (TextReader input)
71 : this (input, new NameTable ())
75 protected XmlTextReader (XmlNameTable nt)
76 : this (String.Empty, null, XmlNodeType.None, null)
80 public XmlTextReader (Stream input, XmlNameTable nt)
81 : this(new XmlStreamReader (input), nt)
85 public XmlTextReader (string url, Stream input)
86 : this (url, new XmlStreamReader (input))
90 public XmlTextReader (string url, TextReader input)
91 : this (url, input, new NameTable ())
95 public XmlTextReader (string url, XmlNameTable nt)
97 Uri uri = resolver.ResolveUri (null, url);
98 Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
99 XmlParserContext ctx = new XmlParserContext (nt,
100 new XmlNamespaceManager (nt),
103 this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document);
106 public XmlTextReader (TextReader input, XmlNameTable nt)
107 : this (String.Empty, input, nt)
111 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
112 : this (context != null ? context.BaseURI : String.Empty,
113 new XmlStreamReader (xmlFragment),
119 public XmlTextReader (string url, Stream input, XmlNameTable nt)
120 : this (url, new XmlStreamReader (input), nt)
124 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
125 : this (url, input, XmlNodeType.Document, null)
129 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
130 : this (context != null ? context.BaseURI : String.Empty,
131 new StringReader (xmlFragment),
137 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
139 InitializeContext (url, context, fragment, fragType);
146 public override int AttributeCount
148 get { return attributeCount; }
151 public override string BaseURI
153 get { return parserContext.BaseURI; }
156 // for XmlReaderSettings.CloseInput support
157 internal bool CloseInput {
158 get { return closeInput; }
159 set { closeInput = value; }
162 public override int Depth
165 int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element ? 0 : -1;
166 if (currentAttributeValue >= 0)
167 return nodeTypeMod + elementDepth + 2; // inside attribute value.
168 else if (currentAttribute >= 0)
169 return nodeTypeMod + elementDepth + 1;
174 public Encoding Encoding
176 get { return parserContext.Encoding; }
180 public EntityHandling EntityHandling {
181 get { throw new NotImplementedException (); }
185 public override bool EOF
190 readState == ReadState.EndOfFile ||
191 readState == ReadState.Closed;
197 public override Evidence Evidence {
198 get { return base.Evidence; }
202 public override bool HasValue {
203 get { return cursorToken.Value != null; }
206 public override bool IsDefault {
207 // XmlTextReader does not expand default attributes.
208 get { return false; }
211 public override bool IsEmptyElement {
212 get { return cursorToken.IsEmptyElement; }
215 public override string this [int i] {
216 get { return GetAttribute (i); }
219 public override string this [string name] {
220 get { return GetAttribute (name); }
223 public override string this [string localName, string namespaceName] {
224 get { return GetAttribute (localName, namespaceName); }
227 public int LineNumber {
229 if (useProceedingLineInfo)
232 return cursorToken.LineNumber;
236 public int LinePosition {
238 if (useProceedingLineInfo)
241 return cursorToken.LinePosition;
245 public override string LocalName {
246 get { return cursorToken.LocalName; }
249 public override string Name {
250 get { return cursorToken.Name; }
253 public bool Namespaces {
254 get { return namespaces; }
256 if (readState != ReadState.Initial)
257 throw new InvalidOperationException ("Namespaces have to be set before reading.");
262 public override string NamespaceURI {
263 get { return cursorToken.NamespaceURI; }
266 public override XmlNameTable NameTable {
267 get { return parserContext.NameTable; }
270 public override XmlNodeType NodeType {
271 get { return cursorToken.NodeType; }
274 public bool Normalization {
275 get { return normalization; }
276 set { normalization = value; }
279 public override string Prefix {
280 get { return cursorToken.Prefix; }
284 public bool ProhibitDtd {
285 get { return prohibitDtd; }
286 set { prohibitDtd = value; }
290 public override char QuoteChar {
291 get { return cursorToken.QuoteChar; }
294 public override ReadState ReadState {
295 get { return readState; }
298 public override string Value {
299 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
302 public WhitespaceHandling WhitespaceHandling {
303 get { return whitespaceHandling; }
304 set { whitespaceHandling = value; }
307 public override string XmlLang {
308 get { return parserContext.XmlLang; }
311 public XmlResolver XmlResolver {
312 set { resolver = value; }
315 public override XmlSpace XmlSpace {
316 get { return parserContext.XmlSpace; }
323 public override void Close ()
325 readState = ReadState.Closed;
327 cursorToken.Clear ();
328 currentToken.Clear ();
330 if (closeInput && reader != null)
334 public override string GetAttribute (int i)
336 if (i >= attributeCount)
337 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
339 return attributeTokens [i].Value;
343 // MS.NET 1.0 msdn says that this method returns String.Empty
344 // for absent attribute, but in fact it returns null.
345 // This description is corrected in MS.NET 1.1 msdn.
346 public override string GetAttribute (string name)
348 for (int i = 0; i < attributeCount; i++)
349 if (attributeTokens [i].Name == name)
350 return attributeTokens [i].Value;
354 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
356 for (int i = 0; i < attributeCount; i++) {
357 XmlAttributeTokenInfo ti = attributeTokens [i];
358 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
364 internal XmlParserContext GetInternalParserContext ()
366 return parserContext;
369 public override string GetAttribute (string localName, string namespaceURI)
371 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
374 return attributeTokens [idx].Value;
377 public TextReader GetRemainder ()
379 if (peekCharsIndex == peekCharsLength)
381 return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
384 bool IXmlLineInfo.HasLineInfo ()
389 public override string LookupNamespace (string prefix)
391 return LookupNamespace (prefix, false);
394 internal string LookupNamespace (string prefix, bool atomizedName)
396 return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
399 public override void MoveToAttribute (int i)
401 if (i >= attributeCount)
402 throw new ArgumentOutOfRangeException ("attribute index out of range.");
404 currentAttribute = i;
405 currentAttributeValue = -1;
406 cursorToken = attributeTokens [i];
409 public override bool MoveToAttribute (string name)
411 for (int i = 0; i < attributeCount; i++) {
412 XmlAttributeTokenInfo ti = attributeTokens [i];
413 if (ti.Name == name) {
421 public override bool MoveToAttribute (string localName, string namespaceName)
423 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
426 MoveToAttribute (idx);
430 public override bool MoveToElement ()
432 if (currentToken == null) // for attribute .ctor()
435 if (cursorToken == currentToken)
438 if (currentAttribute >= 0) {
439 currentAttribute = -1;
440 currentAttributeValue = -1;
441 cursorToken = currentToken;
448 public override bool MoveToFirstAttribute ()
450 if (attributeCount == 0)
453 return MoveToNextAttribute ();
456 public override bool MoveToNextAttribute ()
458 if (currentAttribute == 0 && attributeCount == 0)
460 if (currentAttribute + 1 < attributeCount) {
462 currentAttributeValue = -1;
463 cursorToken = attributeTokens [currentAttribute];
470 public override bool Read ()
472 if (startNodeType == XmlNodeType.Attribute) {
473 if (currentAttribute == 0)
474 return false; // already read.
476 IncrementAttributeToken ();
477 ReadAttributeValueTokens ('"');
478 cursorToken = attributeTokens [0];
479 currentAttributeValue = -1;
480 readState = ReadState.Interactive;
485 readState = ReadState.Interactive;
486 currentLinkedNodeLineNumber = line;
487 currentLinkedNodeLinePosition = column;
488 useProceedingLineInfo = true;
490 cursorToken = currentToken;
492 currentAttribute = currentAttributeValue = -1;
493 currentToken.Clear ();
495 // It was moved from end of ReadStartTag ().
501 if (shouldSkipUntilEndTag) {
502 shouldSkipUntilEndTag = false;
503 return ReadUntilEndTag ();
506 base64CacheStartsAt = -1;
508 more = ReadContent ();
510 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
511 throw new XmlException ("Document element did not appear.");
513 useProceedingLineInfo = false;
517 public override bool ReadAttributeValue ()
519 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
523 if (currentAttribute < 0)
525 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
526 if (currentAttributeValue < 0)
527 currentAttributeValue = ti.ValueTokenStartIndex - 1;
529 if (currentAttributeValue < ti.ValueTokenEndIndex) {
530 currentAttributeValue++;
531 cursorToken = attributeValueTokens [currentAttributeValue];
538 private int SkipIgnorableBase64Chars (char [] chars, int charsLength, int i)
540 while (chars [i] == '=' || XmlChar.IsWhitespace (chars [i]))
541 if (charsLength == ++i)
546 public int ReadBase64 (byte [] buffer, int offset, int length)
549 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
551 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
552 else if (buffer.Length < offset + length)
553 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
555 if (length == 0) // It does not raise an error.
558 int bufIndex = offset;
559 int bufLast = offset + length;
561 if (base64CacheStartsAt >= 0) {
562 for (int i = base64CacheStartsAt; i < 3; i++) {
563 buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
564 if (bufIndex == bufLast)
565 return bufLast - offset;
569 for (int i = 0; i < 3; i++)
571 base64CacheStartsAt = -1;
573 int max = (int) System.Math.Ceiling (4.0 / 3 * length);
574 int additional = max % 4;
576 max += 4 - additional;
577 char [] chars = new char [max];
578 int charsLength = ReadChars (chars, 0, max);
583 for (int i = 0; i < charsLength - 3; i++) {
584 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
586 b = (byte) (GetBase64Byte (chars [i]) << 2);
587 if (bufIndex < bufLast)
588 buffer [bufIndex] = b;
590 if (base64CacheStartsAt < 0)
591 base64CacheStartsAt = 0;
594 // charsLength mod 4 might not equals to 0.
595 if (++i == charsLength)
597 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
599 b = GetBase64Byte (chars [i]);
600 work = (byte) (b >> 4);
601 if (bufIndex < bufLast) {
602 buffer [bufIndex] += work;
606 base64Cache [0] += work;
608 work = (byte) ((b & 0xf) << 4);
609 if (bufIndex < bufLast) {
610 buffer [bufIndex] = work;
613 if (base64CacheStartsAt < 0)
614 base64CacheStartsAt = 1;
615 base64Cache [1] = work;
618 if (++i == charsLength)
620 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
622 b = GetBase64Byte (chars [i]);
623 work = (byte) (b >> 2);
624 if (bufIndex < bufLast) {
625 buffer [bufIndex] += work;
629 base64Cache [1] += work;
631 work = (byte) ((b & 3) << 6);
632 if (bufIndex < bufLast)
633 buffer [bufIndex] = work;
635 if (base64CacheStartsAt < 0)
636 base64CacheStartsAt = 2;
637 base64Cache [2] = work;
639 if (++i == charsLength)
641 if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
643 work = GetBase64Byte (chars [i]);
644 if (bufIndex < bufLast) {
645 buffer [bufIndex] += work;
649 base64Cache [2] += work;
651 return System.Math.Min (bufLast - offset, bufIndex - offset);
654 public int ReadBinHex (byte [] buffer, int offset, int length)
657 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
659 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
660 else if (buffer.Length < offset + length)
661 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
666 char [] chars = new char [length * 2];
667 int charsLength = ReadChars (chars, 0, length * 2);
668 return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
671 public int ReadChars (char [] buffer, int offset, int length)
673 return ReadCharsInternal (buffer, offset, length);
678 public override string ReadInnerXml ()
680 return ReadInnerXmlInternal ();
683 public override string ReadOuterXml ()
685 return ReadOuterXmlInternal ();
688 public override string ReadString ()
690 return ReadStringInternal ();
694 public void ResetState ()
699 public override void ResolveEntity ()
701 // XmlTextReader does not resolve entities.
702 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
706 [MonoTODO ("Implement for performance reason")]
707 public override void Skip ()
715 // Parsed DTD Objects
716 #if DTD_HANDLE_EVENTS
717 internal event ValidationEventHandler ValidationEventHandler;
720 internal DTDObjectModel DTD {
721 get { return parserContext.Dtd; }
724 internal XmlResolver Resolver {
725 get { return resolver; }
730 internal class XmlTokenInfo
732 public XmlTokenInfo (XmlTextReader xtr, bool isPrimaryToken)
734 this.isPrimaryToken = isPrimaryToken;
742 protected XmlTextReader Reader;
745 public string LocalName;
746 public string Prefix;
747 public string NamespaceURI;
748 public bool IsEmptyElement;
749 public char QuoteChar;
750 public int LineNumber;
751 public int LinePosition;
753 public XmlNodeType NodeType;
755 public virtual string Value {
757 if (valueCache != null)
760 case XmlNodeType.Text:
761 case XmlNodeType.SignificantWhitespace:
762 case XmlNodeType.Whitespace:
763 case XmlNodeType.Comment:
764 case XmlNodeType.CDATA:
765 case XmlNodeType.ProcessingInstruction:
766 valueCache = Reader.CreateValueString ();
771 set { valueCache = value; }
774 public virtual void Clear ()
777 NodeType = XmlNodeType.None;
778 Name = LocalName = Prefix = NamespaceURI = String.Empty;
779 IsEmptyElement = false;
781 LineNumber = LinePosition = 0;
784 internal virtual void FillNames ()
786 if (Reader.Namespaces) {
787 int indexOfColon = -1;
789 case XmlNodeType.Attribute:
790 case XmlNodeType.Element:
791 case XmlNodeType.EndElement:
792 indexOfColon = Name.IndexOf (':');
796 if (indexOfColon == -1) {
797 Prefix = String.Empty;
800 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
801 // However, this might be reverted if NameTable is got improved.
802 char [] nameArr = Name.ToCharArray ();
803 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
804 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
805 // Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
806 // LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
811 case XmlNodeType.Attribute:
812 if (Prefix.Length == 0)
813 NamespaceURI = string.Empty;
815 NamespaceURI = Reader.LookupNamespace (Prefix, true);
818 case XmlNodeType.Element:
819 case XmlNodeType.EndElement:
820 NamespaceURI = Reader.LookupNamespace (Prefix, true);
827 Prefix = String.Empty;
833 internal class XmlAttributeTokenInfo : XmlTokenInfo
835 public XmlAttributeTokenInfo (XmlTextReader reader)
836 : base (reader, false)
838 NodeType = XmlNodeType.Attribute;
841 public int ValueTokenStartIndex;
842 public int ValueTokenEndIndex;
844 bool cachedNormalization;
845 StringBuilder tmpBuilder = new StringBuilder ();
847 public override string Value {
849 if (cachedNormalization != Reader.Normalization)
851 if (valueCache != null)
854 cachedNormalization = Reader.Normalization;
856 // An empty value should return String.Empty.
857 if (ValueTokenStartIndex == ValueTokenEndIndex) {
858 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
859 if (ti.NodeType == XmlNodeType.EntityReference)
860 valueCache = String.Concat ("&", ti.Name, ";");
862 valueCache = ti.Value;
863 if (cachedNormalization)
868 tmpBuilder.Length = 0;
869 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
870 XmlTokenInfo ti = Reader.attributeValueTokens [i];
871 if (ti.NodeType == XmlNodeType.Text)
872 tmpBuilder.Append (ti.Value);
874 tmpBuilder.Append ('&');
875 tmpBuilder.Append (ti.Name);
876 tmpBuilder.Append (';');
880 valueCache = tmpBuilder.ToString ();
881 if (cachedNormalization)
886 set { valueCache = value; }
889 public override void Clear ()
893 NodeType = XmlNodeType.Attribute;
894 ValueTokenStartIndex = ValueTokenEndIndex = 0;
897 internal override void FillNames ()
900 if (Prefix == "xmlns" || Name == "xmlns")
901 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
904 private void NormalizeSpaces ()
906 tmpBuilder.Length = 0;
907 for (int i = 0; i < valueCache.Length; i++)
908 switch (valueCache [i]) {
910 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
915 tmpBuilder.Append (' ');
918 tmpBuilder.Append (valueCache [i]);
921 valueCache = tmpBuilder.ToString ();
925 private XmlTokenInfo cursorToken;
926 private XmlTokenInfo currentToken;
927 private XmlAttributeTokenInfo currentAttributeToken;
928 private XmlTokenInfo currentAttributeValueToken;
929 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
930 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
931 private int currentAttribute;
932 private int currentAttributeValue;
933 private int attributeCount;
935 private XmlParserContext parserContext;
937 private ReadState readState;
940 private int elementDepth;
941 private bool depthUp;
943 private bool popScope;
945 private string [] elementNames;
946 int elementNameStackPos;
948 private bool allowMultipleRoot;
950 private bool isStandalone;
952 private bool returnEntityReference;
953 private string entityReferenceName;
955 private char [] nameBuffer;
956 private int nameLength;
957 private int nameCapacity;
958 private const int initialNameCapacity = 32;
960 private char [] valueBuffer;
961 private int valueLength;
962 private int valueCapacity;
963 private const int initialValueCapacity = 256;
965 private char [] currentTagBuffer;
966 private int currentTagLength;
967 private int currentTagCapacity;
968 private const int initialCurrentTagCapacity = 256;
970 private TextReader reader;
971 private char [] peekChars;
972 private int peekCharsIndex;
973 private int peekCharsLength;
974 private const int peekCharCapacity = 1024;
979 private int currentLinkedNodeLineNumber;
980 private int currentLinkedNodeLinePosition;
981 private bool useProceedingLineInfo;
983 private XmlNodeType startNodeType;
984 // State machine attribute.
985 // XmlDeclaration: after the first node.
986 // DocumentType: after doctypedecl
987 // Element: inside document element
988 // EndElement: after document element
989 private XmlNodeType currentState;
991 // For ReadChars()/ReadBase64()/ReadBinHex()
992 private bool shouldSkipUntilEndTag;
993 private byte [] base64Cache = new byte [3];
994 private int base64CacheStartsAt;
996 // These values are never re-initialized.
997 private bool namespaces = true;
998 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
999 private XmlResolver resolver = new XmlUrlResolver ();
1000 private bool normalization = false;
1002 private bool prohibitDtd = false;
1003 private bool closeInput = true;
1005 private void Init ()
1007 currentToken = new XmlTokenInfo (this, true);
1008 cursorToken = currentToken;
1009 currentAttribute = -1;
1010 currentAttributeValue = -1;
1013 readState = ReadState.Initial;
1014 allowMultipleRoot = false;
1020 popScope = allowMultipleRoot = false;
1021 elementNames = new string [10];
1022 elementNameStackPos = 0;
1024 isStandalone = false;
1025 returnEntityReference = false;
1026 entityReferenceName = String.Empty;
1028 nameBuffer = new char [initialNameCapacity];
1030 nameCapacity = initialNameCapacity;
1032 valueBuffer = new char [initialValueCapacity];
1034 valueCapacity = initialValueCapacity;
1036 currentTagBuffer = new char [initialCurrentTagCapacity];
1037 currentTagLength = 0;
1038 currentTagCapacity = initialCurrentTagCapacity;
1041 peekCharsLength = 0;
1042 if (peekChars == null)
1043 peekChars = new char [peekCharCapacity];
1047 currentTagLength = 0;
1049 currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
1050 useProceedingLineInfo = false;
1052 currentState = XmlNodeType.None;
1054 shouldSkipUntilEndTag = false;
1055 base64CacheStartsAt = -1;
1058 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1060 startNodeType = fragType;
1061 parserContext = context;
1062 if (context == null) {
1063 XmlNameTable nt = new NameTable ();
1064 parserContext = new XmlParserContext (nt,
1065 new XmlNamespaceManager (nt),
1070 if (url != null && url.Length > 0) {
1073 uri = new Uri (url);
1074 } catch (Exception) {
1075 string path = Path.GetFullPath ("./a");
1076 uri = new Uri (new Uri (path), url);
1078 parserContext.BaseURI = uri.ToString ();
1086 case XmlNodeType.Attribute:
1087 reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
1089 case XmlNodeType.Element:
1090 currentState = XmlNodeType.Element;
1091 allowMultipleRoot = true;
1093 case XmlNodeType.Document:
1096 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1101 internal ConformanceLevel Conformance {
1103 if (value == ConformanceLevel.Fragment) {
1104 currentState = XmlNodeType.Element;
1105 allowMultipleRoot = true;
1110 internal void AdjustLineInfoOffset (int lineNumberOffset, int linePositionOffset)
1112 line += lineNumberOffset;
1113 column += linePositionOffset;
1116 internal void SetNameTable (XmlNameTable nameTable)
1118 parserContext.NameTable = nameTable;
1122 // Use this method rather than setting the properties
1123 // directly so that all the necessary properties can
1124 // be changed in harmony with each other. Maybe the
1125 // fields should be in a seperate class to help enforce
1127 private void SetProperties (
1128 XmlNodeType nodeType,
1130 bool isEmptyElement,
1132 bool clearAttributes)
1134 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1135 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1136 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1139 private void SetProperties (
1141 XmlNodeType nodeType,
1143 bool isEmptyElement,
1145 bool clearAttributes)
1148 token.NodeType = nodeType;
1150 token.IsEmptyElement = isEmptyElement;
1151 token.Value = value;
1152 this.elementDepth = depth;
1154 if (clearAttributes)
1160 private void ClearAttributes ()
1162 for (int i = 0; i < attributeCount; i++)
1163 attributeTokens [i].Clear ();
1165 currentAttribute = -1;
1166 currentAttributeValue = -1;
1169 private int PeekChar ()
1171 if (peekCharsLength == peekCharsIndex) {
1172 if (!ReadTextReader ())
1177 return peekChars [peekCharsIndex];
1180 private int ReadChar ()
1184 if (peekCharsLength == peekCharsIndex) {
1185 if (!ReadTextReader ())
1189 ch = peekChars [peekCharsIndex++];
1197 if (currentState != XmlNodeType.Element)
1198 AppendCurrentTagChar (ch);
1202 private bool ReadTextReader ()
1205 peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity);
1206 if (peekCharsLength == 0)
1211 private string ExpandSurrogateChar (int ch)
1213 if (ch < Char.MaxValue)
1214 return ((char) ch).ToString ();
1216 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1217 return new string (tmp);
1221 // This should really keep track of some state so
1222 // that it's not possible to have more than one document
1223 // element or text outside of the document element.
1224 private bool ReadContent ()
1226 currentTagLength = 0;
1228 parserContext.NamespaceManager.PopScope ();
1232 if (returnEntityReference)
1233 SetEntityReferenceProperties ();
1235 int c = PeekChar ();
1237 readState = ReadState.EndOfFile;
1238 ClearValueBuffer ();
1240 XmlNodeType.None, // nodeType
1241 String.Empty, // name
1242 false, // isEmptyElement
1244 true // clearAttributes
1247 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1256 case '\r': goto case ' ';
1257 case '\n': goto case ' ';
1258 case '\t': goto case ' ';
1260 if (whitespaceHandling == WhitespaceHandling.All ||
1261 whitespaceHandling == WhitespaceHandling.Significant)
1265 return ReadContent ();
1274 return this.ReadState != ReadState.EndOfFile;
1277 private void SetEntityReferenceProperties ()
1279 DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1280 if (this.isStandalone)
1281 if (DTD == null || decl == null || !decl.IsInternalSubset)
1282 throw new XmlException (this as IXmlLineInfo,
1283 "Standalone document must not contain any references to an non-internally declared entity.");
1284 if (decl != null && decl.NotationName != null)
1285 throw new XmlException (this as IXmlLineInfo,
1286 "Reference to any unparsed entities is not allowed here.");
1288 ClearValueBuffer ();
1290 XmlNodeType.EntityReference, // nodeType
1291 entityReferenceName, // name
1292 false, // isEmptyElement
1294 true // clearAttributes
1297 returnEntityReference = false;
1298 entityReferenceName = String.Empty;
1301 // The leading '<' has already been consumed.
1302 private void ReadTag ()
1304 switch (PeekChar ())
1312 ReadProcessingInstruction ();
1324 // The leading '<' has already been consumed.
1325 private void ReadStartTag ()
1327 if (currentState == XmlNodeType.EndElement)
1328 throw new XmlException (this as IXmlLineInfo,
1329 "Multiple document element was detected.");
1330 currentState = XmlNodeType.Element;
1332 parserContext.NamespaceManager.PushScope ();
1334 string name = ReadName ();
1335 if (currentState == XmlNodeType.EndElement)
1336 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1338 bool isEmptyElement = false;
1343 if (XmlChar.IsFirstNameChar (PeekChar ()))
1344 ReadAttributes (false);
1345 cursorToken = this.currentToken;
1348 for (int i = 0; i < attributeCount; i++)
1349 attributeTokens [i].FillNames ();
1352 for (int i = 0; i < attributeCount; i++) {
1353 for (int j = i + 1; j < attributeCount; j++)
1354 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1355 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1356 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1357 throw new XmlException (this as IXmlLineInfo,
1358 "Attribute name and qualified name must be identical.");
1361 string baseUri = GetAttribute ("xml:base");
1362 if (baseUri != null) {
1363 if (this.resolver != null)
1364 parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1366 parserContext.BaseURI = baseUri;
1368 string xmlLang = GetAttribute ("xml:lang");
1369 if (xmlLang != null)
1370 parserContext.XmlLang = xmlLang;
1371 string xmlSpaceAttr = GetAttribute ("xml:space");
1372 if (xmlSpaceAttr != null) {
1373 if (xmlSpaceAttr == "preserve")
1374 parserContext.XmlSpace = XmlSpace.Preserve;
1375 else if (xmlSpaceAttr == "default")
1376 parserContext.XmlSpace = XmlSpace.Default;
1378 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1380 if (PeekChar () == '/') {
1382 isEmptyElement = true;
1387 PushElementName (name);
1388 parserContext.PushScope ();
1393 XmlNodeType.Element, // nodeType
1395 isEmptyElement, // isEmptyElement
1397 false // clearAttributes
1400 if (LookupNamespace (Prefix) == null)
1401 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1403 for (int i = 0; i < attributeCount; i++) {
1404 MoveToAttribute (i);
1405 if (LookupNamespace (Prefix) == null)
1406 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1413 CheckCurrentStateUpdate ();
1416 private void PushElementName (string name)
1418 if (elementNames.Length == elementNameStackPos) {
1419 string [] newArray = new string [elementNames.Length * 2];
1420 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1421 elementNames = newArray;
1423 elementNames [elementNameStackPos++] = name;
1426 // The reader is positioned on the first character
1427 // of the element's name.
1428 private void ReadEndTag ()
1430 if (currentState != XmlNodeType.Element)
1431 throw new XmlException (this as IXmlLineInfo,
1432 "End tag cannot appear in this state.");
1434 string name = ReadName ();
1435 if (elementNameStackPos == 0)
1436 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1437 string expected = elementNames [--elementNameStackPos];
1438 if (expected != name)
1439 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1440 parserContext.PopScope ();
1442 ExpectAfterWhitespace ('>');
1447 XmlNodeType.EndElement, // nodeType
1449 false, // isEmptyElement
1451 true // clearAttributes
1456 CheckCurrentStateUpdate ();
1459 private void CheckCurrentStateUpdate ()
1461 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1462 currentState = XmlNodeType.EndElement;
1465 private void AppendNameChar (int ch)
1467 if (nameLength == nameCapacity)
1468 ExpandNameCapacity ();
1469 if (ch < Char.MaxValue)
1470 nameBuffer [nameLength++] = (char) ch;
1472 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1473 if (nameLength == nameCapacity)
1474 ExpandNameCapacity ();
1475 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1479 private void ExpandNameCapacity ()
1481 nameCapacity = nameCapacity * 2;
1482 char [] oldNameBuffer = nameBuffer;
1483 nameBuffer = new char [nameCapacity];
1484 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1487 private string CreateNameString ()
1489 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1492 private void AppendValueChar (int ch)
1494 if (valueLength == valueCapacity)
1495 ExpandValueCapacity ();
1496 if (ch < Char.MaxValue)
1497 valueBuffer [valueLength++] = (char) ch;
1499 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1500 if (valueLength == valueCapacity)
1501 ExpandValueCapacity ();
1502 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1506 private void ExpandValueCapacity ()
1508 valueCapacity = valueCapacity * 2;
1509 char [] oldValueBuffer = valueBuffer;
1510 valueBuffer = new char [valueCapacity];
1511 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1514 private string CreateValueString ()
1516 return new string (valueBuffer, 0, valueLength);
1519 private void ClearValueBuffer ()
1524 private void AppendCurrentTagChar (int ch)
1526 if (currentTagLength == currentTagCapacity)
1527 ExpandCurrentTagCapacity ();
1528 if (ch < Char.MaxValue)
1529 currentTagBuffer [currentTagLength++] = (char) ch;
1531 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1532 if (currentTagLength == currentTagCapacity)
1533 ExpandCurrentTagCapacity ();
1534 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1538 private void ExpandCurrentTagCapacity ()
1540 currentTagCapacity = currentTagCapacity * 2;
1541 char [] oldCurrentTagBuffer = currentTagBuffer;
1542 currentTagBuffer = new char [currentTagCapacity];
1543 Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1546 private string CreateCurrentTagString ()
1548 return new string (currentTagBuffer, 0, currentTagLength);
1551 private void ClearCurrentTagBuffer ()
1553 currentTagLength = 0;
1556 // The reader is positioned on the first character
1558 private void ReadText (bool notWhitespace)
1560 if (currentState != XmlNodeType.Element)
1561 throw new XmlException (this as IXmlLineInfo,
1562 "Text node cannot appear in this state.");
1565 ClearValueBuffer ();
1567 int ch = PeekChar ();
1568 bool previousWasCloseBracket = false;
1570 while (ch != '<' && ch != -1) {
1573 ch = ReadReference (false);
1574 if (returnEntityReference) // Returns -1 if char validation should not be done
1578 if (XmlChar.IsInvalid (ch))
1579 throw new XmlException (this, "Not allowed character was found.");
1583 AppendValueChar (ch);
1587 if (previousWasCloseBracket)
1588 if (PeekChar () == '>')
1589 throw new XmlException (this as IXmlLineInfo,
1590 "Inside text content, character sequence ']]>' is not allowed.");
1591 previousWasCloseBracket = true;
1593 else if (previousWasCloseBracket)
1594 previousWasCloseBracket = false;
1596 notWhitespace = true;
1599 if (returnEntityReference && valueLength == 0) {
1600 SetEntityReferenceProperties ();
1602 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1603 this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1605 nodeType, // nodeType
1606 String.Empty, // name
1607 false, // isEmptyElement
1608 null, // value: create only when required
1609 true // clearAttributes
1614 // The leading '&' has already been consumed.
1615 // Returns true if the entity reference isn't a simple
1616 // character reference or one of the predefined entities.
1617 // This allows the ReadText method to break so that the
1618 // next call to Read will return the EntityReference node.
1619 private int ReadReference (bool ignoreEntityReferences)
1621 if (PeekChar () == '#') {
1623 return ReadCharacterReference ();
1625 return ReadEntityReference (ignoreEntityReferences);
1628 private int ReadCharacterReference ()
1632 if (PeekChar () == 'x') {
1635 while (PeekChar () != ';' && PeekChar () != -1) {
1636 int ch = ReadChar ();
1638 if (ch >= '0' && ch <= '9')
1639 value = (value << 4) + ch - '0';
1640 else if (ch >= 'A' && ch <= 'F')
1641 value = (value << 4) + ch - 'A' + 10;
1642 else if (ch >= 'a' && ch <= 'f')
1643 value = (value << 4) + ch - 'a' + 10;
1645 throw new XmlException (this as IXmlLineInfo,
1646 String.Format (CultureInfo.InvariantCulture,
1647 "invalid hexadecimal digit: {0} (#x{1:X})",
1652 while (PeekChar () != ';' && PeekChar () != -1) {
1653 int ch = ReadChar ();
1655 if (ch >= '0' && ch <= '9')
1656 value = value * 10 + ch - '0';
1658 throw new XmlException (this as IXmlLineInfo,
1659 String.Format (CultureInfo.InvariantCulture,
1660 "invalid decimal digit: {0} (#x{1:X})",
1668 // There is no way to save surrogate pairs...
1669 if (normalization && XmlChar.IsInvalid (value))
1670 throw new XmlException (this as IXmlLineInfo,
1671 "Referenced character was not allowed in XML.");
1675 // Returns -1 if it should not be validated.
1676 // Real EOF must not be detected here.
1677 private int ReadEntityReference (bool ignoreEntityReferences)
1679 string name = ReadName ();
1682 int predefined = XmlChar.GetPredefinedEntity (name);
1683 if (predefined >= 0)
1686 if (ignoreEntityReferences) {
1687 AppendValueChar ('&');
1688 for (int i = 0; i < name.Length; i++)
1689 AppendValueChar (name [i]);
1690 AppendValueChar (';');
1692 returnEntityReference = true;
1693 entityReferenceName = name;
1699 // The reader is positioned on the first character of
1700 // the attribute name.
1701 private void ReadAttributes (bool isXmlDecl)
1704 bool requireWhitespace = false;
1705 currentAttribute = -1;
1706 currentAttributeValue = -1;
1709 if (!SkipWhitespace () && requireWhitespace)
1710 throw new XmlException ("Unexpected token. Name is required here.");
1712 IncrementAttributeToken ();
1713 currentAttributeToken.LineNumber = line;
1714 currentAttributeToken.LinePosition = column;
1716 currentAttributeToken.LocalName =
1717 currentAttributeToken.Name = ReadName ();
1718 ExpectAfterWhitespace ('=');
1720 ReadAttributeValueTokens (-1);
1723 if (currentAttributeToken.Name == "xmlns")
1724 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1725 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1726 string nsPrefix = currentAttributeToken.Name.Substring (6);
1727 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1730 if (!SkipWhitespace ())
1731 requireWhitespace = true;
1732 peekChar = PeekChar ();
1734 if (peekChar == '?')
1737 else if (peekChar == '/' || peekChar == '>')
1739 } while (peekChar != -1);
1741 currentAttribute = -1;
1742 currentAttributeValue = -1;
1745 private void AddAttribute (string name, string value)
1747 IncrementAttributeToken ();
1748 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1749 ati.Name = "SYSTEM";
1751 IncrementAttributeValueToken ();
1752 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1754 SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1758 private void IncrementAttributeToken ()
1761 if (attributeTokens.Length == currentAttribute) {
1762 XmlAttributeTokenInfo [] newArray =
1763 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1764 attributeTokens.CopyTo (newArray, 0);
1765 attributeTokens = newArray;
1767 if (attributeTokens [currentAttribute] == null)
1768 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1769 currentAttributeToken = attributeTokens [currentAttribute];
1770 currentAttributeToken.Clear ();
1773 private void IncrementAttributeValueToken ()
1775 ClearValueBuffer ();
1776 currentAttributeValue++;
1777 if (attributeValueTokens.Length == currentAttributeValue) {
1778 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1779 attributeValueTokens.CopyTo (newArray, 0);
1780 attributeValueTokens = newArray;
1782 if (attributeValueTokens [currentAttributeValue] == null)
1783 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1784 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1785 currentAttributeValueToken.Clear ();
1788 // LAMESPEC: Orthodox XML reader should normalize attribute values
1789 private void ReadAttributeValueTokens (int dummyQuoteChar)
1791 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1793 if (quoteChar != '\'' && quoteChar != '\"')
1794 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1795 currentAttributeToken.QuoteChar = (char) quoteChar;
1797 IncrementAttributeValueToken ();
1798 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1799 currentAttributeValueToken.LineNumber = line;
1800 currentAttributeValueToken.LinePosition = column;
1802 bool incrementToken = false;
1803 bool isNewToken = true;
1808 if (ch == quoteChar)
1811 if (incrementToken) {
1812 IncrementAttributeValueToken ();
1813 currentAttributeValueToken.LineNumber = line;
1814 currentAttributeValueToken.LinePosition = column;
1815 incrementToken = false;
1822 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1824 if (dummyQuoteChar < 0)
1825 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1826 else // Attribute value constructor.
1830 int startPosition = currentTagLength - 1;
1831 if (PeekChar () == '#') {
1833 ch = ReadCharacterReference ();
1834 if (normalization && XmlChar.IsInvalid (ch))
1835 throw new XmlException (this as IXmlLineInfo,
1836 "Not allowed character was found.");
1837 AppendValueChar (ch);
1840 // Check XML 1.0 section 3.1 WFC.
1841 string entName = ReadName ();
1843 int predefined = XmlChar.GetPredefinedEntity (entName);
1844 if (predefined < 0) {
1845 CheckAttributeEntityReferenceWFC (entName);
1846 currentAttributeValueToken.Value = CreateValueString ();
1847 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1849 IncrementAttributeValueToken ();
1850 currentAttributeValueToken.Name = entName;
1851 currentAttributeValueToken.Value = String.Empty;
1852 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1853 incrementToken = true;
1856 AppendValueChar (predefined);
1859 if (normalization && XmlChar.IsInvalid (ch))
1860 throw new XmlException (this, "Invalid character was found.");
1861 AppendValueChar (ch);
1867 if (!incrementToken) {
1868 currentAttributeValueToken.Value = CreateValueString ();
1869 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1871 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1875 private void CheckAttributeEntityReferenceWFC (string entName)
1877 DTDEntityDeclaration entDecl =
1878 DTD == null ? null : DTD.EntityDecls [entName];
1879 if (DTD != null && resolver != null && entDecl == null)
1880 throw new XmlException (this, "Referenced entity does not exist.");
1882 if (entDecl == null)
1885 if (entDecl.HasExternalReference)
1886 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
1887 if (isStandalone && !entDecl.IsInternalSubset)
1888 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
1889 if (entDecl.EntityValue.IndexOf ('<') >= 0)
1890 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1893 // The reader is positioned on the first character
1896 // It may be xml declaration or processing instruction.
1897 private void ReadProcessingInstruction ()
1899 string target = ReadName ();
1900 if (target == "xml") {
1901 ReadXmlDeclaration ();
1903 } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml")
1904 throw new XmlException (this as IXmlLineInfo,
1905 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1907 if (currentState == XmlNodeType.None)
1908 currentState = XmlNodeType.XmlDeclaration;
1910 if (!SkipWhitespace ())
1911 if (PeekChar () != '?')
1912 throw new XmlException (this as IXmlLineInfo,
1913 "Invalid processing instruction name was found.");
1915 ClearValueBuffer ();
1917 while (PeekChar () != -1) {
1918 int ch = ReadChar ();
1920 if (ch == '?' && PeekChar () == '>') {
1925 if (normalization && XmlChar.IsInvalid (ch))
1926 throw new XmlException (this, "Invalid character was found.");
1927 AppendValueChar (ch);
1931 XmlNodeType.ProcessingInstruction, // nodeType
1933 false, // isEmptyElement
1934 null, // value: create only when required
1935 true // clearAttributes
1939 // The reader is positioned after "<?xml "
1940 private void ReadXmlDeclaration ()
1942 if (currentState != XmlNodeType.None) {
1943 throw new XmlException (this as IXmlLineInfo,
1944 "XML declaration cannot appear in this state.");
1946 currentState = XmlNodeType.XmlDeclaration;
1950 ReadAttributes (true); // They must have "version."
1951 string version = GetAttribute ("version");
1953 string message = null;
1955 if (attributeTokens [0].Name != "version" || version != "1.0")
1956 message = "Version 1.0 declaration is required in XML Declaration.";
1957 else if (attributeCount > 1 &&
1958 (attributeTokens [1].Name != "encoding" &&
1959 attributeTokens [1].Name != "standalone"))
1960 message = "Invalid Xml Declaration markup was found.";
1961 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1962 message = "Invalid Xml Declaration markup was found.";
1963 string sa = GetAttribute ("standalone");
1964 if (sa != null && sa != "yes" && sa != "no")
1965 message = "Only 'yes' or 'no' is allowed for standalone.";
1967 this.isStandalone = (sa == "yes");
1969 if (message != null)
1970 throw new XmlException (this as IXmlLineInfo, message);
1973 XmlNodeType.XmlDeclaration, // nodeType
1975 false, // isEmptyElement
1976 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1977 false // clearAttributes
1983 internal void SkipTextDeclaration ()
1985 this.currentState = XmlNodeType.Element;
1987 if (PeekChar () != '<')
1992 if (PeekChar () != '?') {
1998 while (peekCharsIndex < 6) {
1999 if (PeekChar () < 0)
2004 if (new string (peekChars, 2, 4) != "xml ") {
2005 if (new string (peekChars, 2, 3).ToLower (CultureInfo.InvariantCulture) == "xml") {
2006 throw new XmlException (this as IXmlLineInfo,
2007 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
2016 if (PeekChar () == 'v') {
2018 ExpectAfterWhitespace ('=');
2020 int quoteChar = ReadChar ();
2021 char [] expect1_0 = new char [3];
2022 int versionLength = 0;
2023 switch (quoteChar) {
2026 while (PeekChar () != quoteChar) {
2027 if (PeekChar () == -1)
2028 throw new XmlException (this as IXmlLineInfo,
2029 "Invalid version declaration inside text declaration.");
2030 else if (versionLength == 3)
2031 throw new XmlException (this as IXmlLineInfo,
2032 "Invalid version number inside text declaration.");
2034 expect1_0 [versionLength] = (char) ReadChar ();
2036 if (versionLength == 3 && new String (expect1_0) != "1.0")
2037 throw new XmlException (this as IXmlLineInfo,
2038 "Invalid version number inside text declaration.");
2045 throw new XmlException (this as IXmlLineInfo,
2046 "Invalid version declaration inside text declaration.");
2050 if (PeekChar () == 'e') {
2051 Expect ("encoding");
2052 ExpectAfterWhitespace ('=');
2054 int quoteChar = ReadChar ();
2055 switch (quoteChar) {
2058 while (PeekChar () != quoteChar)
2059 if (ReadChar () == -1)
2060 throw new XmlException (this as IXmlLineInfo,
2061 "Invalid encoding declaration inside text declaration.");
2066 throw new XmlException (this as IXmlLineInfo,
2067 "Invalid encoding declaration inside text declaration.");
2069 // Encoding value should be checked inside XmlInputStream.
2072 throw new XmlException (this as IXmlLineInfo,
2073 "Encoding declaration is mandatory in text declaration.");
2078 // The reader is positioned on the first character after
2079 // the leading '<!'.
2080 private void ReadDeclaration ()
2082 int ch = PeekChar ();
2100 throw new XmlException (this as IXmlLineInfo,
2101 "Unexpected declaration markup was found.");
2105 // The reader is positioned on the first character after
2106 // the leading '<!--'.
2107 private void ReadComment ()
2109 if (currentState == XmlNodeType.None)
2110 currentState = XmlNodeType.XmlDeclaration;
2112 ClearValueBuffer ();
2114 while (PeekChar () != -1) {
2115 int ch = ReadChar ();
2117 if (ch == '-' && PeekChar () == '-') {
2120 if (PeekChar () != '>')
2121 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2127 if (XmlChar.IsInvalid (ch))
2128 throw new XmlException (this as IXmlLineInfo,
2129 "Not allowed character was found.");
2131 AppendValueChar (ch);
2135 XmlNodeType.Comment, // nodeType
2136 String.Empty, // name
2137 false, // isEmptyElement
2138 null, // value: create only when required
2139 true // clearAttributes
2143 // The reader is positioned on the first character after
2144 // the leading '<![CDATA['.
2145 private void ReadCDATA ()
2147 if (currentState != XmlNodeType.Element)
2148 throw new XmlException (this as IXmlLineInfo,
2149 "CDATA section cannot appear in this state.");
2151 ClearValueBuffer ();
2155 while (PeekChar () != -1) {
2160 if (ch == ']' && PeekChar () == ']') {
2161 ch = ReadChar (); // ']'
2163 if (PeekChar () == '>') {
2170 if (normalization && XmlChar.IsInvalid (ch))
2171 throw new XmlException (this, "Invalid character was found.");
2173 AppendValueChar (ch);
2177 XmlNodeType.CDATA, // nodeType
2178 String.Empty, // name
2179 false, // isEmptyElement
2180 null, // value: create only when required
2181 true // clearAttributes
2185 // The reader is positioned on the first character after
2186 // the leading '<!DOCTYPE'.
2187 private void ReadDoctypeDecl ()
2190 throw new XmlException (this as IXmlLineInfo,
2191 "Document Type Declaration (DTD) is prohibited in this XML.");
2192 switch (currentState) {
2193 case XmlNodeType.DocumentType:
2194 case XmlNodeType.Element:
2195 case XmlNodeType.EndElement:
2196 throw new XmlException (this as IXmlLineInfo,
2197 "Document type cannot appear in this state.");
2199 currentState = XmlNodeType.DocumentType;
2201 string doctypeName = null;
2202 string publicId = null;
2203 string systemId = null;
2204 int intSubsetStartLine = 0;
2205 int intSubsetStartColumn = 0;
2208 doctypeName = ReadName ();
2213 systemId = ReadSystemLiteral (true);
2216 publicId = ReadPubidLiteral ();
2217 if (!SkipWhitespace ())
2218 throw new XmlException (this as IXmlLineInfo,
2219 "Whitespace is required between PUBLIC id and SYSTEM id.");
2220 systemId = ReadSystemLiteral (false);
2226 if(PeekChar () == '[')
2228 // read markupdecl etc. or end of decl
2230 intSubsetStartLine = this.LineNumber;
2231 intSubsetStartColumn = this.LinePosition;
2232 int startPos = currentTagLength;
2233 ReadInternalSubset ();
2234 int endPos = currentTagLength - 1;
2235 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2237 // end of DOCTYPE decl.
2238 ExpectAfterWhitespace ('>');
2240 GenerateDTDObjectModel (doctypeName, publicId,
2241 systemId, parserContext.InternalSubset,
2242 intSubsetStartLine, intSubsetStartColumn);
2244 // set properties for <!DOCTYPE> node
2246 XmlNodeType.DocumentType, // nodeType
2247 doctypeName, // name
2248 false, // isEmptyElement
2249 parserContext.InternalSubset, // value
2250 true // clearAttributes
2253 if (publicId != null)
2254 AddAttribute ("PUBLIC", publicId);
2255 if (systemId != null)
2256 AddAttribute ("SYSTEM", systemId);
2257 currentAttribute = currentAttributeValue = -1;
2260 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2261 string systemId, string internalSubset)
2263 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2266 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2267 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2270 parserContext.Dtd = new DTDObjectModel (this.NameTable); // merges both internal and external subsets in the meantime,
2271 DTD.BaseURI = BaseURI;
2273 DTD.PublicId = publicId;
2274 DTD.SystemId = systemId;
2275 DTD.InternalSubset = internalSubset;
2276 DTD.XmlResolver = resolver;
2277 DTD.IsStandalone = isStandalone;
2278 DTD.LineNumber = line;
2279 DTD.LinePosition = column;
2281 DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2282 dr.Normalization = this.normalization;
2283 #if DTD_HANDLE_EVENTS
2284 dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2286 return dr.GenerateDTDObjectModel ();
2289 private void OnValidationEvent (object o, ValidationEventArgs e)
2291 #if DTD_HANDLE_EVENTS
2292 if (ValidationEventHandler != null)
2293 // Override object as this.
2294 ValidationEventHandler (this, e);
2298 private enum DtdInputState
2311 private class DtdInputStateStack
2313 Stack intern = new Stack ();
2314 public DtdInputStateStack ()
2316 Push (DtdInputState.Free);
2319 public DtdInputState Peek ()
2321 return (DtdInputState) intern.Peek ();
2324 public DtdInputState Pop ()
2326 return (DtdInputState) intern.Pop ();
2329 public void Push (DtdInputState val)
2336 DtdInputStateStack stateStack = new DtdInputStateStack ();
2337 DtdInputState State {
2338 get { return stateStack.Peek (); }
2341 // Simply read but not generate any result.
2342 private void ReadInternalSubset ()
2344 bool continueParse = true;
2346 while (continueParse) {
2347 switch (ReadChar ()) {
2350 case DtdInputState.Free:
2351 continueParse = false;
2353 case DtdInputState.InsideDoubleQuoted:
2355 case DtdInputState.InsideSingleQuoted:
2358 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2362 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2364 if (State == DtdInputState.InsideDoubleQuoted ||
2365 State == DtdInputState.InsideSingleQuoted)
2366 continue; // well-formed
2367 switch (ReadChar ()) {
2369 stateStack.Push (DtdInputState.PI);
2372 switch (ReadChar ()) {
2374 switch (ReadChar ()) {
2377 stateStack.Push (DtdInputState.ElementDecl);
2381 stateStack.Push (DtdInputState.EntityDecl);
2384 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2389 stateStack.Push (DtdInputState.AttlistDecl);
2393 stateStack.Push (DtdInputState.NotationDecl);
2397 stateStack.Push (DtdInputState.Comment);
2402 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2406 if (State == DtdInputState.InsideSingleQuoted)
2408 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2409 stateStack.Push (DtdInputState.InsideSingleQuoted);
2412 if (State == DtdInputState.InsideDoubleQuoted)
2414 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2415 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2419 case DtdInputState.ElementDecl:
2420 goto case DtdInputState.NotationDecl;
2421 case DtdInputState.AttlistDecl:
2422 goto case DtdInputState.NotationDecl;
2423 case DtdInputState.EntityDecl:
2424 goto case DtdInputState.NotationDecl;
2425 case DtdInputState.NotationDecl:
2428 case DtdInputState.InsideDoubleQuoted:
2430 case DtdInputState.InsideSingleQuoted:
2431 continue; // well-formed
2432 case DtdInputState.Comment:
2435 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2439 if (State == DtdInputState.PI) {
2440 if (ReadChar () == '>')
2445 if (State == DtdInputState.Comment) {
2446 if (PeekChar () == '-') {
2454 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2455 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2461 // The reader is positioned on the first 'S' of "SYSTEM".
2462 private string ReadSystemLiteral (bool expectSYSTEM)
2466 if (!SkipWhitespace ())
2467 throw new XmlException (this as IXmlLineInfo,
2468 "Whitespace is required after 'SYSTEM'.");
2472 int quoteChar = ReadChar (); // apos or quot
2473 int startPos = currentTagLength;
2475 ClearValueBuffer ();
2476 while (c != quoteChar) {
2479 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2481 AppendValueChar (c);
2483 return CreateValueString ();
2486 private string ReadPubidLiteral()
2489 if (!SkipWhitespace ())
2490 throw new XmlException (this as IXmlLineInfo,
2491 "Whitespace is required after 'PUBLIC'.");
2492 int quoteChar = ReadChar ();
2493 int startPos = currentTagLength;
2495 ClearValueBuffer ();
2496 while(c != quoteChar)
2499 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2500 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2501 throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2503 AppendValueChar (c);
2505 return CreateValueString ();
2508 // The reader is positioned on the first character
2510 private string ReadName ()
2512 int ch = PeekChar ();
2513 if (!XmlChar.IsFirstNameChar (ch))
2514 throw new XmlException (this as IXmlLineInfo,String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
2518 AppendNameChar (ReadChar ());
2520 while (XmlChar.IsNameChar (PeekChar ())) {
2521 AppendNameChar (ReadChar ());
2524 return CreateNameString ();
2527 // Read the next character and compare it against the
2528 // specified character.
2529 private void Expect (int expected)
2531 int ch = ReadChar ();
2533 if (ch != expected) {
2534 throw new XmlException (this as IXmlLineInfo,
2535 String.Format (CultureInfo.InvariantCulture,
2536 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2544 private void Expect (string expected)
2546 int len = expected.Length;
2547 for(int i=0; i< len; i++)
2548 Expect (expected[i]);
2551 private void ExpectAfterWhitespace (char c)
2554 int i = ReadChar ();
2555 if (i < 0x21 && XmlChar.IsWhitespace (i))
2558 throw new XmlException (this, String.Format (CultureInfo.InvariantCulture, "Expected {0}, but found {1} [{2}]", c, (char) i, i));
2563 // Does not consume the first non-whitespace character.
2564 private bool SkipWhitespace ()
2566 bool skipped = XmlChar.IsWhitespace (PeekChar ());
2569 while (XmlChar.IsWhitespace (PeekChar ()))
2574 private void ReadWhitespace ()
2576 if (currentState == XmlNodeType.None)
2577 currentState = XmlNodeType.XmlDeclaration;
2579 ClearValueBuffer ();
2580 int ch = PeekChar ();
2582 AppendValueChar (ReadChar ());
2583 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2585 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2588 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2589 XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2590 SetProperties (nodeType,
2593 null, // value: create only when required
2600 // Since ReadBase64() is processed for every 4 chars, it does
2601 // not handle '=' here.
2602 private byte GetBase64Byte (char ch)
2610 if (ch >= 'A' && ch <= 'Z')
2611 return (byte) (ch - 'A');
2612 else if (ch >= 'a' && ch <= 'z')
2613 return (byte) (ch - 'a' + 26);
2614 else if (ch >= '0' && ch <= '9')
2615 return (byte) (ch - '0' + 52);
2617 throw new XmlException ("Invalid Base64 character was found.");
2621 // Returns -1 if it should throw an error.
2622 private int ReadCharsInternal (char [] buffer, int offset, int length)
2624 if (IsEmptyElement) {
2630 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2631 else if (length < 0)
2632 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2633 else if (buffer.Length < offset + length)
2634 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2636 if (NodeType != XmlNodeType.Element)
2639 shouldSkipUntilEndTag = true;
2641 int bufIndex = offset;
2642 for (int i = 0; i < length; i++) {
2643 int c = PeekChar ();
2646 throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2649 if (PeekChar () != '/') {
2650 buffer [bufIndex++] = '<';
2653 // Seems to skip immediate EndElement
2660 shouldSkipUntilEndTag = false;
2661 Read (); // move to the next node
2665 if (c < Char.MaxValue)
2666 buffer [bufIndex++] = (char) c;
2668 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2669 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2677 private bool ReadUntilEndTag ()
2684 throw new XmlException (this as IXmlLineInfo,
2685 "Unexpected end of xml.");
2687 if (PeekChar () != '/')
2690 string name = ReadName ();
2691 if (name != elementNames [elementNameStackPos - 1])
2695 elementNames [--elementNameStackPos] = null;