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;
28 using System.Xml.Schema;
30 using Mono.Xml.Native;
34 public class XmlTextReader : XmlReader, IXmlLineInfo
38 protected XmlTextReader ()
42 public XmlTextReader (Stream input)
43 : this (new XmlStreamReader (input))
47 public XmlTextReader (string url)
48 : this(url, new NameTable ())
52 public XmlTextReader (TextReader input)
53 : this (input, new NameTable ())
57 protected XmlTextReader (XmlNameTable nt)
58 : this (String.Empty, null, XmlNodeType.None, null)
62 public XmlTextReader (Stream input, XmlNameTable nt)
63 : this(new XmlStreamReader (input), nt)
67 public XmlTextReader (string url, Stream input)
68 : this (url, new XmlStreamReader (input))
72 public XmlTextReader (string url, TextReader input)
73 : this (url, input, new NameTable ())
77 public XmlTextReader (string url, XmlNameTable nt)
78 : this (url, new XmlStreamReader (url, null, null), nt)
82 public XmlTextReader (TextReader input, XmlNameTable nt)
83 : this (String.Empty, input, nt)
87 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
88 : this (context != null ? context.BaseURI : String.Empty,
89 new XmlStreamReader (xmlFragment),
95 public XmlTextReader (string url, Stream input, XmlNameTable nt)
96 : this (url, new XmlStreamReader (input), nt)
100 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
101 : this (url, input, XmlNodeType.Document, null)
105 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
106 : this (context != null ? context.BaseURI : String.Empty,
107 new StringReader (xmlFragment),
113 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
115 InitializeContext (url, context, fragment, fragType);
122 public override int AttributeCount
124 get { return attributeCount; }
127 public override string BaseURI
129 get { return parserContext.BaseURI; }
132 public override int Depth
135 if (currentAttributeValue >= 0)
136 return elementDepth + 2; // inside attribute value.
137 else if (currentAttribute >= 0)
138 return elementDepth + 1;
143 public Encoding Encoding
145 get { return parserContext.Encoding; }
148 public override bool EOF
153 readState == ReadState.EndOfFile ||
154 readState == ReadState.Closed;
158 public override bool HasValue
161 if (this.valueBuilderAvailable)
162 return valueBuilder.Length != 0;
164 return cursorToken.Value != null;
168 public override bool IsDefault
172 // XmlTextReader does not expand default attributes.
177 public override bool IsEmptyElement
179 get { return cursorToken.IsEmptyElement; }
182 public override string this [int i]
184 get { return GetAttribute (i); }
187 public override string this [string name]
189 get { return GetAttribute (name); }
192 public override string this [string localName, string namespaceName]
194 get { return GetAttribute (localName, namespaceName); }
197 public int LineNumber
200 if (useProceedingLineInfo)
201 return currentInput.LineNumber;
203 return cursorToken.LineNumber;
207 public int LinePosition
210 if (useProceedingLineInfo)
211 return currentInput.LinePosition;
213 return cursorToken.LinePosition;
217 public override string LocalName
219 get { return cursorToken.LocalName; }
222 public override string Name
224 get { return cursorToken.Name; }
227 public bool Namespaces
229 get { return namespaces; }
231 if (readState != ReadState.Initial)
232 throw new InvalidOperationException ("Namespaces have to be set before reading.");
237 public override string NamespaceURI
239 get { return cursorToken.NamespaceURI; }
242 public override XmlNameTable NameTable
244 get { return parserContext.NameTable; }
247 public override XmlNodeType NodeType
249 get { return cursorToken.NodeType; }
253 public bool Normalization
255 get { return normalization; }
256 set { normalization = value; }
259 public override string Prefix
261 get { return cursorToken.Prefix; }
264 public override char QuoteChar
266 get { return cursorToken.QuoteChar; }
269 public override ReadState ReadState
271 get { return readState; }
274 public override string Value
276 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
279 public WhitespaceHandling WhitespaceHandling
281 get { return whitespaceHandling; }
282 set { whitespaceHandling = value; }
285 public override string XmlLang
287 get { return parserContext.XmlLang; }
290 public XmlResolver XmlResolver
292 set { resolver = value; }
295 public override XmlSpace XmlSpace
297 get { return parserContext.XmlSpace; }
304 public override void Close ()
306 readState = ReadState.Closed;
307 foreach (XmlParserInput input in parserInputStack.ToArray ())
309 this.currentInput.Close ();
311 cursorToken.Clear ();
312 currentToken.Clear ();
316 public override string GetAttribute (int i)
318 if (i > attributeCount)
319 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
321 return attributeTokens [i].Value;
325 // MS.NET 1.0 msdn says that this method returns String.Empty
326 // for absent attribute, but in fact it returns null.
327 // This description is corrected in MS.NET 1.1 msdn.
328 public override string GetAttribute (string name)
330 for (int i = 0; i < attributeCount; i++)
331 if (attributeTokens [i].Name == name)
332 return attributeTokens [i].Value;
336 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
338 for (int i = 0; i < attributeCount; i++) {
339 XmlAttributeTokenInfo ti = attributeTokens [i];
340 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
346 internal XmlParserContext GetInternalParserContext ()
348 return parserContext;
351 public override string GetAttribute (string localName, string namespaceURI)
353 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
356 return attributeTokens [idx].Value;
360 public TextReader GetRemainder ()
362 throw new NotImplementedException ();
365 bool IXmlLineInfo.HasLineInfo ()
370 public override string LookupNamespace (string prefix)
372 return parserContext.NamespaceManager.LookupNamespace (prefix);
375 public override void MoveToAttribute (int i)
377 if (i >= attributeCount)
378 throw new ArgumentOutOfRangeException ("attribute index out of range.");
380 currentAttribute = i;
381 currentAttributeValue = -1;
382 cursorToken = attributeTokens [i];
385 public override bool MoveToAttribute (string name)
387 for (int i = 0; i < attributeCount; i++) {
388 XmlAttributeTokenInfo ti = attributeTokens [i];
389 if (ti.Name == name) {
397 public override bool MoveToAttribute (string localName, string namespaceName)
399 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
402 MoveToAttribute (idx);
406 public override bool MoveToElement ()
408 if (currentToken == null) // for attribute .ctor()
411 if (currentAttribute >= 0) {
412 currentAttribute = -1;
413 currentAttributeValue = -1;
414 cursorToken = currentToken;
421 public override bool MoveToFirstAttribute ()
423 if (attributeCount == 0)
426 return MoveToNextAttribute ();
429 public override bool MoveToNextAttribute ()
431 if (currentAttribute == 0 && attributeCount == 0)
433 if (currentAttribute + 1 < attributeCount) {
435 currentAttributeValue = -1;
436 cursorToken = attributeTokens [currentAttribute];
443 public override bool Read ()
445 if (startNodeType == XmlNodeType.Attribute) {
446 if (currentAttribute == 0)
447 return false; // already read.
449 IncrementAttributeToken ();
450 ReadAttributeValueTokens ('"');
451 cursorToken = attributeTokens [0];
452 currentAttributeValue = -1;
453 readState = ReadState.Interactive;
458 readState = ReadState.Interactive;
459 currentLinkedNodeLineNumber = currentInput.LineNumber;
460 currentLinkedNodeLinePosition = currentInput.LinePosition;
461 useProceedingLineInfo = true;
463 cursorToken = currentToken;
465 currentAttribute = currentAttributeValue = -1;
466 currentToken.Clear ();
468 // It was moved from end of ReadStartTag ().
473 more = ReadContent ();
475 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
476 currentState = XmlNodeType.EndElement;
477 if (maybeTextDecl != 0)
480 if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
481 throw new XmlException ("Document element did not appear.");
483 useProceedingLineInfo = false;
487 public override bool ReadAttributeValue ()
489 if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
493 if (currentAttribute < 0)
495 XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
496 if (currentAttributeValue < 0)
497 currentAttributeValue = ti.ValueTokenStartIndex - 1;
499 if (currentAttributeValue < ti.ValueTokenEndIndex) {
500 currentAttributeValue++;
501 cursorToken = attributeValueTokens [currentAttributeValue];
509 public int ReadBase64 (byte [] buffer, int offset, int length)
511 throw new NotImplementedException ();
515 public int ReadBinHex (byte [] buffer, int offset, int length)
517 throw new NotImplementedException ();
521 public int ReadChars (char [] buffer, int offset, int length)
523 throw new NotImplementedException ();
527 public override string ReadInnerXml ()
529 if (readState != ReadState.Interactive)
533 case XmlNodeType.Attribute:
534 return value.Substring (1, value.Length - 2);
535 case XmlNodeType.Element:
539 int startDepth = depth;
541 if (innerXmlBuilder == null)
542 innerXmlBuilder = new StringBuilder ();
543 innerXmlBuilder.Length = 0;
547 if (NodeType ==XmlNodeType.None)
548 throw new XmlException ("unexpected end of xml.");
549 else if (NodeType == XmlNodeType.EndElement && depth == startDepth) {
554 innerXmlBuilder.Append (currentTag);
556 string xml = innerXmlBuilder.ToString ();
557 innerXmlBuilder.Length = 0;
559 case XmlNodeType.None:
560 // MS document is incorrect. Seems not to progress.
568 public override string ReadOuterXml ()
570 if (readState != ReadState.Interactive)
574 case XmlNodeType.Attribute:
575 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
576 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
577 case XmlNodeType.Element:
578 bool isEmpty = IsEmptyElement;
579 string startTag = currentTag.ToString ();
582 if (NodeType == XmlNodeType.Element && !isEmpty)
583 return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
585 return currentTag.ToString ();
586 case XmlNodeType.None:
587 // MS document is incorrect. Seems not to progress.
596 public override string ReadString ()
598 return ReadStringInternal ();
601 public void ResetState ()
606 public override void ResolveEntity ()
608 // XmlTextReader does not resolve entities.
609 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
615 // Parsed DTD Objects
616 internal DTDObjectModel DTD {
617 get { return parserContext.Dtd; }
620 internal bool MaybeTextDecl {
621 set { if (value) this.maybeTextDecl = 2; }
626 internal class XmlTokenInfo
628 public XmlTokenInfo (XmlTextReader xtr)
636 protected XmlTextReader Reader;
639 public string LocalName;
640 public string Prefix;
641 public string NamespaceURI;
642 public bool IsEmptyElement;
643 public char QuoteChar;
644 public int LineNumber;
645 public int LinePosition;
647 public XmlNodeType NodeType;
649 public virtual string Value {
651 if (valueCache != null)
653 else if (Reader.valueBuilderAvailable) {
654 valueCache = Reader.valueBuilder.ToString ();
664 public virtual void Clear ()
667 NodeType = XmlNodeType.None;
668 Name = LocalName = Prefix = NamespaceURI = String.Empty;
669 IsEmptyElement = false;
671 LineNumber = LinePosition = 0;
674 internal virtual void FillNames ()
676 if (Reader.Namespaces) {
677 int indexOfColon = Name.IndexOf (':');
679 if (indexOfColon == -1) {
680 Prefix = String.Empty;
683 Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
684 LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
689 case XmlNodeType.Attribute:
690 if (Prefix == string.Empty)
691 NamespaceURI = string.Empty;
693 NamespaceURI = Reader.LookupNamespace (Prefix);
696 case XmlNodeType.Element:
697 case XmlNodeType.EndElement:
698 NamespaceURI = Reader.LookupNamespace (Prefix);
705 Prefix = String.Empty;
711 internal class XmlAttributeTokenInfo : XmlTokenInfo
713 public XmlAttributeTokenInfo (XmlTextReader reader)
716 NodeType = XmlNodeType.Attribute;
719 public int ValueTokenStartIndex;
720 public int ValueTokenEndIndex;
723 public override string Value {
725 if (valueCache != null)
727 // An empty value should return String.Empty.
728 if (ValueTokenStartIndex == ValueTokenEndIndex) {
729 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
730 if (ti.NodeType == XmlNodeType.Text)
731 valueCache = ti.Value;
733 valueCache = String.Concat ("&", ti.Name, ";");
737 StringBuilder sb = new StringBuilder ();
738 for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
739 XmlTokenInfo ti = Reader.attributeValueTokens [i];
740 if (ti.NodeType == XmlNodeType.Text)
741 sb.Append (ti.Value);
749 valueCache = sb.ToString ();
758 public override void Clear ()
762 NodeType = XmlNodeType.Attribute;
763 ValueTokenStartIndex = ValueTokenEndIndex = 0;
766 internal override void FillNames ()
769 if (Prefix == "xmlns" || Name == "xmlns")
770 NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
774 private XmlTokenInfo cursorToken;
775 private XmlTokenInfo currentToken;
776 private XmlAttributeTokenInfo currentAttributeToken;
777 private XmlTokenInfo currentAttributeValueToken;
778 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
779 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
780 private int currentAttribute;
781 private int currentAttributeValue;
782 private int attributeCount;
784 private XmlParserContext parserContext;
786 private XmlParserInput currentInput;
787 private Stack parserInputStack;
788 private ReadState readState;
791 private int elementDepth;
792 private bool depthUp;
794 private bool popScope;
795 private Stack elementStack;
796 private bool allowMultipleRoot;
798 private bool isStandalone;
800 private StringBuilder valueBuilder;
801 private bool valueBuilderAvailable = false;
803 private bool returnEntityReference;
804 private string entityReferenceName;
806 private char [] nameBuffer;
807 private int nameLength;
808 private int nameCapacity;
809 private const int initialNameCapacity = 256;
811 private StringBuilder valueBuffer;
813 private int currentLinkedNodeLineNumber;
814 private int currentLinkedNodeLinePosition;
815 private bool useProceedingLineInfo;
817 // A buffer for ReadContent for ReadOuterXml
818 private StringBuilder currentTag {
820 return currentInput.CurrentMarkup;
824 // Parameter entity placeholder
825 private Hashtable parameterEntities;
826 private int dtdIncludeSect;
828 private XmlNodeType startNodeType;
829 // State machine attribute.
830 // XmlDeclaration: after the first node.
831 // DocumentType: after doctypedecl
832 // Element: inside document element
833 // EndElement: after document element
834 private XmlNodeType currentState;
835 private int maybeTextDecl;
837 private XmlResolver resolver = new XmlUrlResolver ();
839 // These values are never re-initialized.
840 private bool namespaces = true;
841 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
842 private bool normalization = false;
846 readState = ReadState.Initial;
847 currentState = XmlNodeType.None;
849 allowMultipleRoot = false;
855 parserInputStack = new Stack ();
856 elementStack = new Stack();
857 currentAttribute = -1;
858 currentAttributeValue = -1;
860 returnEntityReference = false;
861 entityReferenceName = String.Empty;
863 nameBuffer = new char [initialNameCapacity];
865 nameCapacity = initialNameCapacity;
867 valueBuffer = new StringBuilder (512);
868 parameterEntities = new Hashtable ();
870 currentToken = new XmlTokenInfo (this);
871 cursorToken = currentToken;
874 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
876 startNodeType = fragType;
877 parserContext = context;
878 if (context == null) {
879 XmlNameTable nt = new NameTable ();
880 parserContext = new XmlParserContext (nt,
881 new XmlNamespaceManager (nt),
886 if (url != null && url != String.Empty) {
887 string path = Path.GetFullPath ("./a");
888 Uri uri = new Uri (new Uri (path), url);
889 parserContext.BaseURI = uri.ToString ();
895 case XmlNodeType.Attribute:
896 fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
898 case XmlNodeType.Element:
899 currentState = XmlNodeType.Element;
900 allowMultipleRoot = true;
902 case XmlNodeType.Document:
905 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
908 this.currentInput = new XmlParserInput (fragment, url);
911 // Use this method rather than setting the properties
912 // directly so that all the necessary properties can
913 // be changed in harmony with each other. Maybe the
914 // fields should be in a seperate class to help enforce
916 private void SetProperties (
917 XmlNodeType nodeType,
921 bool clearAttributes)
923 SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
924 currentToken.LineNumber = this.currentLinkedNodeLineNumber;
925 currentToken.LinePosition = this.currentLinkedNodeLinePosition;
928 private void SetProperties (
930 XmlNodeType nodeType,
934 bool clearAttributes)
936 this.valueBuilderAvailable = false;
938 token.NodeType = nodeType;
940 token.IsEmptyElement = isEmptyElement;
942 this.elementDepth = depth;
950 private void SetProperties (
951 XmlNodeType nodeType,
954 bool clearAttributes,
955 StringBuilder value) {
956 SetProperties (nodeType, name, isEmptyElement, (string)null, clearAttributes);
957 this.valueBuilderAvailable = true;
958 this.valueBuilder = value;
961 private void ClearAttributes ()
963 for (int i = 0; i < attributeCount; i++)
964 attributeTokens [i].Clear ();
966 currentAttribute = -1;
967 currentAttributeValue = -1;
970 private int PeekChar ()
972 return currentInput.PeekChar ();
975 private int ReadChar ()
977 return currentInput.ReadChar ();
980 // This should really keep track of some state so
981 // that it's not possible to have more than one document
982 // element or text outside of the document element.
983 private bool ReadContent ()
985 currentTag.Length = 0;
987 parserContext.NamespaceManager.PopScope ();
991 if (returnEntityReference)
992 SetEntityReferenceProperties ();
994 switch (PeekChar ()) {
999 case '\r': goto case ' ';
1000 case '\n': goto case ' ';
1001 case '\t': goto case ' ';
1003 if (whitespaceHandling == WhitespaceHandling.All ||
1004 whitespaceHandling == WhitespaceHandling.Significant)
1008 return ReadContent ();
1013 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1014 readState = ReadState.EndOfFile;
1016 XmlNodeType.None, // nodeType
1017 String.Empty, // name
1018 false, // isEmptyElement
1019 (string) null, // value
1020 true // clearAttributes
1028 if (NodeType == XmlNodeType.XmlDeclaration && maybeTextDecl == 1)
1029 return ReadContent ();
1030 return this.ReadState != ReadState.EndOfFile;
1033 private void SetEntityReferenceProperties ()
1036 if (resolver != null) {
1038 throw new XmlException (this as IXmlLineInfo,
1039 "Entity reference is not allowed without document type declaration.");
1040 else if((!DTD.InternalSubsetHasPEReference || isStandalone) &&
1041 DTD.EntityDecls [entityReferenceName] == null)
1042 throw new XmlException (this as IXmlLineInfo,
1043 "Required entity declaration for '" + entityReferenceName + "' was not found.");
1044 string dummy = DTD.EntityDecls [entityReferenceName].EntityValue;
1048 XmlNodeType.EntityReference, // nodeType
1049 entityReferenceName, // name
1050 false, // isEmptyElement
1051 (string) null, // value
1052 true // clearAttributes
1055 returnEntityReference = false;
1056 entityReferenceName = String.Empty;
1059 // The leading '<' has already been consumed.
1060 private void ReadTag ()
1062 switch (PeekChar ())
1070 ReadProcessingInstruction ();
1082 // The leading '<' has already been consumed.
1083 private void ReadStartTag ()
1085 if (currentState == XmlNodeType.EndElement)
1086 throw new XmlException (this as IXmlLineInfo,
1087 "Element cannot appear in this state.");
1088 currentState = XmlNodeType.Element;
1090 parserContext.NamespaceManager.PushScope ();
1092 string name = ReadName ();
1093 if (currentState == XmlNodeType.EndElement)
1094 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1096 bool isEmptyElement = false;
1101 if (XmlChar.IsFirstNameChar (PeekChar ()))
1102 ReadAttributes (false);
1103 cursorToken = this.currentToken;
1106 for (int i = 0; i < attributeCount; i++)
1107 attributeTokens [i].FillNames ();
1110 for (int i = 0; i < attributeCount; i++)
1111 for (int j = i + 1; j < attributeCount; j++)
1112 if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1113 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1114 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1115 throw new XmlException (this as IXmlLineInfo,
1116 "Attribute name and qualified name must be identical.");
1118 string baseUri = GetAttribute ("xml:base");
1119 if (baseUri != null)
1120 parserContext.BaseURI = baseUri;
1121 string xmlLang = GetAttribute ("xml:lang");
1122 if (xmlLang != null)
1123 parserContext.XmlLang = xmlLang;
1124 string xmlSpaceAttr = GetAttribute ("xml:space");
1125 if (xmlSpaceAttr != null) {
1126 if (xmlSpaceAttr == "preserve")
1127 parserContext.XmlSpace = XmlSpace.Preserve;
1128 else if (xmlSpaceAttr == "default")
1129 parserContext.XmlSpace = XmlSpace.Default;
1131 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1133 if (PeekChar () == '/') {
1135 isEmptyElement = true;
1140 elementStack.Push (name);
1141 parserContext.PushScope ();
1147 XmlNodeType.Element, // nodeType
1149 isEmptyElement, // isEmptyElement
1150 (string) null, // value
1151 false // clearAttributes
1155 // The reader is positioned on the first character
1156 // of the element's name.
1157 private void ReadEndTag ()
1159 if (currentState != XmlNodeType.Element)
1160 throw new XmlException (this as IXmlLineInfo,
1161 "End tag cannot appear in this state.");
1163 string name = ReadName ();
1164 if (elementStack.Count == 0)
1165 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1166 string expected = (string)elementStack.Pop();
1167 if (expected != name)
1168 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1169 parserContext.PopScope ();
1177 XmlNodeType.EndElement, // nodeType
1179 false, // isEmptyElement
1180 (string) null, // value
1181 true // clearAttributes
1187 private void AppendNameChar (int ch)
1189 CheckNameCapacity ();
1190 nameBuffer [nameLength++] = (char)ch;
1193 private void CheckNameCapacity ()
1195 if (nameLength == nameCapacity) {
1196 nameCapacity = nameCapacity * 2;
1197 char [] oldNameBuffer = nameBuffer;
1198 nameBuffer = new char [nameCapacity];
1199 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1203 private string CreateNameString ()
1205 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1208 private void AppendValueChar (int ch)
1210 valueBuffer.Append ((char)ch);
1213 private string CreateValueString ()
1215 return valueBuffer.ToString ();
1218 private void ClearValueBuffer ()
1220 valueBuffer.Length = 0;
1223 // The reader is positioned on the first character
1225 private void ReadText (bool notWhitespace)
1227 if (currentState != XmlNodeType.Element)
1228 throw new XmlException (this as IXmlLineInfo,
1229 "Text node cannot appear in this state.");
1232 ClearValueBuffer ();
1234 int ch = PeekChar ();
1235 int previousCloseBracketLine = 0;
1236 int previousCloseBracketColumn = 0;
1238 while (ch != '<' && ch != -1) {
1241 if (ReadReference (false))
1244 if (XmlConstructs.IsInvalid (ch))
1245 throw new XmlException (this as IXmlLineInfo,
1246 "Not allowed character was found.");
1247 AppendValueChar (ReadChar ());
1249 if (previousCloseBracketColumn == LinePosition - 1 &&
1250 previousCloseBracketLine == LineNumber)
1251 if (PeekChar () == '>')
1252 throw new XmlException (this as IXmlLineInfo,
1253 "Inside text content, character sequence ']]>' is not allowed.");
1254 previousCloseBracketColumn = LinePosition;
1255 previousCloseBracketLine = LineNumber;
1259 notWhitespace = true;
1262 if (returnEntityReference && valueBuffer.Length == 0) {
1263 SetEntityReferenceProperties ();
1265 XmlNodeType nodeType = notWhitespace ?
1266 XmlNodeType.Text : XmlNodeType.Whitespace;
1268 nodeType, // nodeType
1269 String.Empty, // name
1270 false, // isEmptyElement
1271 true, // clearAttributes
1272 valueBuffer // value
1277 // The leading '&' has already been consumed.
1278 // Returns true if the entity reference isn't a simple
1279 // character reference or one of the predefined entities.
1280 // This allows the ReadText method to break so that the
1281 // next call to Read will return the EntityReference node.
1282 private bool ReadReference (bool ignoreEntityReferences)
1284 if (PeekChar () == '#') {
1286 ReadCharacterReference ();
1288 ReadEntityReference (ignoreEntityReferences);
1290 return returnEntityReference;
1293 private void ReadCharacterReference ()
1297 if (PeekChar () == 'x') {
1300 while (PeekChar () != ';' && PeekChar () != -1) {
1301 int ch = ReadChar ();
1303 if (ch >= '0' && ch <= '9')
1304 value = (value << 4) + ch - '0';
1305 else if (ch >= 'A' && ch <= 'F')
1306 value = (value << 4) + ch - 'A' + 10;
1307 else if (ch >= 'a' && ch <= 'f')
1308 value = (value << 4) + ch - 'a' + 10;
1310 throw new XmlException (this as IXmlLineInfo,
1312 "invalid hexadecimal digit: {0} (#x{1:X})",
1317 while (PeekChar () != ';' && PeekChar () != -1) {
1318 int ch = ReadChar ();
1320 if (ch >= '0' && ch <= '9')
1321 value = value * 10 + ch - '0';
1323 throw new XmlException (this as IXmlLineInfo,
1325 "invalid decimal digit: {0} (#x{1:X})",
1333 // FIXME: how to handle such chars larger than 0xffff?
1334 if (value < 0xffff && !XmlConstructs.IsValid (value))
1335 throw new XmlException (this as IXmlLineInfo,
1336 "Referenced character was not allowed in XML.");
1337 AppendValueChar (value);
1340 private void ReadEntityReference (bool ignoreEntityReferences)
1344 int ch = PeekChar ();
1346 while (ch != ';' && ch != -1) {
1347 AppendNameChar (ReadChar ());
1353 string name = CreateNameString ();
1354 if (!XmlChar.IsName (name))
1355 throw new XmlException (this as IXmlLineInfo,
1356 "Invalid entity reference name was found.");
1358 char predefined = XmlChar.GetPredefinedEntity (name);
1359 if (predefined != 0)
1360 AppendValueChar (predefined);
1362 if (ignoreEntityReferences) {
1363 AppendValueChar ('&');
1365 foreach (char ch2 in name) {
1366 AppendValueChar (ch2);
1369 AppendValueChar (';');
1371 returnEntityReference = true;
1372 entityReferenceName = name;
1377 // The reader is positioned on the first character of
1378 // the attribute name.
1379 private void ReadAttributes (bool endsWithQuestion)
1382 bool requireWhitespace = false;
1383 currentAttribute = -1;
1384 currentAttributeValue = -1;
1387 if (!SkipWhitespace () && requireWhitespace)
1388 throw new XmlException ("Unexpected token. Name is required here.");
1390 IncrementAttributeToken ();
1391 currentAttributeToken.LineNumber = currentInput.LineNumber;
1392 currentAttributeToken.LinePosition = currentInput.LinePosition;
1394 currentAttributeToken.Name = ReadName ();
1398 ReadAttributeValueTokens (-1);
1401 if (currentAttributeToken.Name == "xmlns")
1402 parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1403 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1404 string nsPrefix = NameTable.Add (currentAttributeToken.Name.Substring (6));
1405 parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1408 if (!SkipWhitespace ())
1409 requireWhitespace = true;
1410 peekChar = PeekChar ();
1411 if (endsWithQuestion) {
1412 if (peekChar == '?')
1415 else if (peekChar == '/' || peekChar == '>')
1417 } while (peekChar != -1);
1419 currentAttribute = -1;
1420 currentAttributeValue = -1;
1423 private void AddAttribute (string name, string value)
1425 IncrementAttributeToken ();
1426 XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1427 ati.Name = "SYSTEM";
1429 IncrementAttributeValueToken ();
1430 XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1432 SetProperties (vti, XmlNodeType.Text, name, false, value, false);
1436 private void IncrementAttributeToken ()
1439 if (attributeTokens.Length == currentAttribute) {
1440 XmlAttributeTokenInfo [] newArray =
1441 new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1442 attributeTokens.CopyTo (newArray, 0);
1443 attributeTokens = newArray;
1445 if (attributeTokens [currentAttribute] == null)
1446 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1447 currentAttributeToken = attributeTokens [currentAttribute];
1448 currentAttributeToken.Clear ();
1451 private void IncrementAttributeValueToken ()
1453 ClearValueBuffer ();
1454 currentAttributeValue++;
1455 if (attributeValueTokens.Length == currentAttributeValue) {
1456 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1457 attributeValueTokens.CopyTo (newArray, 0);
1458 attributeValueTokens = newArray;
1460 if (attributeValueTokens [currentAttributeValue] == null)
1461 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this);
1462 currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1463 currentAttributeValueToken.Clear ();
1466 private void ReadAttributeValueTokens (int dummyQuoteChar)
1468 int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1470 if (quoteChar != '\'' && quoteChar != '\"')
1471 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1472 currentAttributeToken.QuoteChar = (char) quoteChar;
1474 IncrementAttributeValueToken ();
1475 currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1476 currentAttributeValueToken.LineNumber = currentInput.LineNumber;
1477 currentAttributeValueToken.LinePosition = currentInput.LinePosition;
1479 bool incrementToken = false;
1480 bool isNewToken = true;
1482 while (loop && PeekChar () != quoteChar) {
1483 if (incrementToken) {
1484 IncrementAttributeValueToken ();
1485 currentAttributeValueToken.LineNumber = currentInput.LineNumber;
1486 currentAttributeValueToken.LinePosition = currentInput.LinePosition;
1487 incrementToken = false;
1491 int ch = ReadChar ();
1496 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1498 if (dummyQuoteChar < 0)
1499 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1500 else // Attribute value constructor.
1504 int startPosition = currentTag.Length - 1;
1505 if (PeekChar () == '#') {
1507 this.ReadCharacterReference ();
1510 // Check XML 1.0 section 3.1 WFC.
1511 string entName = ReadName ();
1513 int predefined = XmlChar.GetPredefinedEntity (entName);
1514 if (predefined == 0) {
1515 DTDEntityDeclaration entDecl =
1516 DTD == null ? null : DTD.EntityDecls [entName];
1517 if (entDecl != null && entDecl.SystemId != null)
1518 // if (!startNodeType == XmlNodeType.Attribute && (entDecl == null || entDecl.SystemId != null))
1519 throw new XmlException (this as IXmlLineInfo,
1520 "Reference to external entities is not allowed in the value of an attribute.");
1521 currentAttributeValueToken.Value = CreateValueString ();
1522 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1524 IncrementAttributeValueToken ();
1525 currentAttributeValueToken.Name = entName;
1526 currentAttributeValueToken.Value = String.Empty;
1527 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1528 incrementToken = true;
1531 AppendValueChar (predefined);
1534 AppendValueChar (ch);
1540 if (!incrementToken) {
1541 currentAttributeValueToken.Value = CreateValueString ();
1542 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1543 currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1546 if (dummyQuoteChar < 0)
1547 ReadChar (); // quoteChar
1550 // The reader is positioned on the quote character.
1551 // *Keeps quote char* to value to get_QuoteChar() correctly.
1552 // Not it is used only for DTD.
1553 private string ReadAttribute (bool isDefaultValue)
1555 ClearValueBuffer ();
1557 int quoteChar = ReadChar ();
1559 if (quoteChar != '\'' && quoteChar != '\"')
1560 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1562 AppendValueChar (quoteChar);
1564 while (PeekChar () != quoteChar) {
1565 int ch = ReadChar ();
1570 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1572 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1575 if (isDefaultValue) {
1576 AppendValueChar (ch);
1579 AppendValueChar (ch);
1580 if (PeekChar () == '#')
1582 // Check XML 1.0 section 3.1 WFC.
1583 string entName = ReadName ();
1585 if (XmlChar.GetPredefinedEntity (entName) == 0) {
1586 DTDEntityDeclaration entDecl =
1587 DTD == null ? null : DTD.EntityDecls [entName];
1588 if (entDecl == null || entDecl.SystemId != null)
1589 throw new XmlException (this as IXmlLineInfo,
1590 "Reference to external entities is not allowed in attribute value.");
1592 valueBuffer.Append (entName);
1593 AppendValueChar (';');
1597 AppendValueChar (ch);
1602 ReadChar (); // quoteChar
1603 AppendValueChar (quoteChar);
1605 return CreateValueString ();
1608 // The reader is positioned on the first character
1611 // It may be xml declaration or processing instruction.
1612 private void ReadProcessingInstruction ()
1614 string target = ReadName ();
1615 if (target == "xml") {
1616 ReadXmlDeclaration ();
1618 } else if (target.ToLower () == "xml")
1619 throw new XmlException (this as IXmlLineInfo,
1620 "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1622 if (currentState == XmlNodeType.None)
1623 currentState = XmlNodeType.XmlDeclaration;
1625 if (!SkipWhitespace ())
1626 if (PeekChar () != '?')
1627 throw new XmlException (this as IXmlLineInfo,
1628 "Invalid processing instruction name was found.");
1630 ClearValueBuffer ();
1632 while (PeekChar () != -1) {
1633 int ch = ReadChar ();
1635 if (ch == '?' && PeekChar () == '>') {
1640 AppendValueChar ((char)ch);
1644 XmlNodeType.ProcessingInstruction, // nodeType
1646 false, // isEmptyElement
1647 true, // clearAttributes
1648 valueBuffer // value
1652 // The reader is positioned after "<?xml "
1653 private void ReadXmlDeclaration ()
1655 if (currentState != XmlNodeType.None) {
1656 if (maybeTextDecl == 0)
1657 throw new XmlException (this as IXmlLineInfo,
1658 "XML declaration cannot appear in this state.");
1660 // Is this required?
1661 if (maybeTextDecl != 0)
1662 currentState = XmlNodeType.XmlDeclaration;
1666 ReadAttributes (true); // They must have "version."
1667 string version = GetAttribute ("version");
1669 string message = null;
1670 if (parserInputStack.Count == 0) {
1671 if (maybeTextDecl == 0 && (attributeTokens [0].Name != "version" || version != "1.0"))
1672 message = "Version 1.0 declaration is required in XML Declaration.";
1673 else if (attributeCount > 1 &&
1674 (attributeTokens [1].Name != "encoding" &&
1675 attributeTokens [1].Name != "standalone"))
1676 message = "Invalid Xml Declaration markup was found.";
1677 else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1678 message = "Invalid Xml Declaration markup was found.";
1679 string sa = GetAttribute ("standalone");
1680 if (sa != null && sa != "yes" && sa != "no")
1681 message = "Only 'yes' or 'no' is allowed for standalone.";
1683 this.isStandalone = (sa == "yes");
1685 int currentCheck = 0;
1686 if (attributeTokens [0].Name == "version") {
1687 if (version != "1.0")
1688 message = "Version 1.0 declaration is required in Text Declaration.";
1691 if (attributeCount <= currentCheck || attributeTokens [currentCheck].Name != "encoding")
1692 message = "Invalid Text Declaration markup was found. encoding specification is required.";
1694 if (message != null)
1695 throw new XmlException (this as IXmlLineInfo, message);
1699 if (maybeTextDecl != 0)
1700 if (this ["standalone"] != null)
1701 throw new XmlException (this as IXmlLineInfo,
1702 "Invalid text declaration.");
1703 if (maybeTextDecl == 2)
1707 XmlNodeType.XmlDeclaration, // nodeType
1709 false, // isEmptyElement
1710 currentInput.CurrentMarkup.ToString (6, currentInput.CurrentMarkup.Length - 6), // value
1711 false // clearAttributes
1715 // The reader is positioned on the first character after
1716 // the leading '<!'.
1717 private void ReadDeclaration ()
1719 int ch = PeekChar ();
1737 throw new XmlException (this as IXmlLineInfo,
1738 "Unexpected declaration markup was found.");
1742 // The reader is positioned on the first character after
1743 // the leading '<!--'.
1744 private void ReadComment ()
1746 if (currentState == XmlNodeType.None)
1747 currentState = XmlNodeType.XmlDeclaration;
1749 ClearValueBuffer ();
1751 while (PeekChar () != -1) {
1752 int ch = ReadChar ();
1754 if (ch == '-' && PeekChar () == '-') {
1757 if (PeekChar () != '>')
1758 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
1764 if (XmlConstructs.IsInvalid (ch))
1765 throw new XmlException (this as IXmlLineInfo,
1766 "Not allowed character was found.");
1768 AppendValueChar ((char)ch);
1772 XmlNodeType.Comment, // nodeType
1773 String.Empty, // name
1774 false, // isEmptyElement
1775 true, // clearAttributes
1776 valueBuffer // value
1780 // The reader is positioned on the first character after
1781 // the leading '<![CDATA['.
1782 private void ReadCDATA ()
1784 if (currentState != XmlNodeType.Element)
1785 throw new XmlException (this as IXmlLineInfo,
1786 "CDATA section cannot appear in this state.");
1788 ClearValueBuffer ();
1792 while (PeekChar () != -1) {
1797 if (ch == ']' && PeekChar () == ']') {
1798 ch = ReadChar (); // ']'
1800 if (PeekChar () == '>') {
1805 // AppendValueChar (']');
1806 // AppendValueChar (']');
1807 // ch = ReadChar ();
1811 AppendValueChar ((char)ch);
1815 XmlNodeType.CDATA, // nodeType
1816 String.Empty, // name
1817 false, // isEmptyElement
1818 true, // clearAttributes
1819 valueBuffer // value
1823 // The reader is positioned on the first character after
1824 // the leading '<!DOCTYPE'.
1825 private void ReadDoctypeDecl ()
1827 switch (currentState) {
1828 case XmlNodeType.DocumentType:
1829 case XmlNodeType.Element:
1830 case XmlNodeType.EndElement:
1831 throw new XmlException (this as IXmlLineInfo,
1832 "Document type cannot appear in this state.");
1834 currentState = XmlNodeType.DocumentType;
1836 string doctypeName = null;
1837 string publicId = null;
1838 string systemId = null;
1839 int intSubsetStartLine = 0;
1840 int intSubsetStartColumn = 0;
1843 doctypeName = ReadName ();
1848 systemId = ReadSystemLiteral (true);
1851 publicId = ReadPubidLiteral ();
1852 if (!SkipWhitespace ())
1853 throw new XmlException (this as IXmlLineInfo,
1854 "Whitespace is required between PUBLIC id and SYSTEM id.");
1855 systemId = ReadSystemLiteral (false);
1861 if(PeekChar () == '[')
1863 // read markupdecl etc. or end of decl
1865 intSubsetStartLine = this.LineNumber;
1866 intSubsetStartColumn = this.LinePosition;
1867 int startPos = currentTag.Length;
1868 ReadInternalSubset ();
1869 int endPos = currentTag.Length - 1;
1870 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1872 // end of DOCTYPE decl.
1876 GenerateDTDObjectModel (doctypeName, publicId,
1877 systemId, parserContext.InternalSubset,
1878 intSubsetStartLine, intSubsetStartColumn);
1880 // set properties for <!DOCTYPE> node
1882 XmlNodeType.DocumentType, // nodeType
1883 doctypeName, // name
1884 false, // isEmptyElement
1885 parserContext.InternalSubset, // value
1886 true // clearAttributes
1889 if (publicId != null)
1890 AddAttribute ("PUBLIC", publicId);
1891 if (systemId != null)
1892 AddAttribute ("SYSTEM", systemId);
1895 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
1896 string systemId, string internalSubset)
1898 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
1901 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
1902 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
1905 parserContext.Dtd = new DTDObjectModel (); // merges both internal and external subsets in the meantime,
1906 DTD.BaseURI = BaseURI;
1908 DTD.PublicId = publicId;
1909 DTD.SystemId = systemId;
1910 DTD.InternalSubset = internalSubset;
1911 DTD.XmlResolver = resolver;
1912 int originalParserDepth = parserInputStack.Count;
1914 if (internalSubset != null && internalSubset.Length > 0) {
1915 XmlParserInput original = currentInput;
1916 currentInput = new XmlParserInput (new StringReader (internalSubset), BaseURI, intSubsetStartLine, intSubsetStartColumn);
1918 more = CompileDTDSubset ();
1919 if (PeekChar () == -1 && parserInputStack.Count > 0)
1921 } while (more || parserInputStack.Count > originalParserDepth);
1922 if (dtdIncludeSect != 0)
1923 throw new XmlException (this as IXmlLineInfo,"INCLUDE section is not ended correctly.");
1924 currentInput = original;
1926 if (systemId != null && systemId != String.Empty && resolver != null) {
1927 PushParserInput (systemId);
1929 more = this.CompileDTDSubset ();
1930 if (PeekChar () == -1 && parserInputStack.Count > 1)
1932 } while (more || parserInputStack.Count > originalParserDepth + 1);
1939 private void PushParserInput (string url)
1943 baseUri = new Uri (BaseURI);
1944 } catch (UriFormatException) {
1947 Uri absUri = resolver.ResolveUri (baseUri, url);
1948 string absPath = absUri.ToString ();
1950 foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1951 if (i.BaseURI == absPath)
1952 throw new XmlException (this as IXmlLineInfo, "Nested inclusion is not allowed: " + url);
1954 parserInputStack.Push (currentInput);
1955 currentInput = new XmlParserInput (new XmlStreamReader (url, false, resolver, BaseURI), absPath);
1956 parserContext.PushScope ();
1957 parserContext.BaseURI = absPath;
1962 private void PopParserInput ()
1964 currentInput = parserInputStack.Pop () as XmlParserInput;
1965 parserContext.PopScope ();
1968 private enum DtdInputState
1981 private class DtdInputStateStack
1983 Stack intern = new Stack ();
1984 public DtdInputStateStack ()
1986 Push (DtdInputState.Free);
1989 public DtdInputState Peek ()
1991 return (DtdInputState) intern.Peek ();
1994 public DtdInputState Pop ()
1996 return (DtdInputState) intern.Pop ();
1999 public void Push (DtdInputState val)
2006 DtdInputStateStack stateStack = new DtdInputStateStack ();
2007 DtdInputState State {
2008 get { return stateStack.Peek (); }
2011 // Simply read but not generate any result.
2012 private void ReadInternalSubset ()
2014 bool continueParse = true;
2016 while (continueParse) {
2017 switch (ReadChar ()) {
2020 case DtdInputState.Free:
2021 continueParse = false;
2023 case DtdInputState.InsideDoubleQuoted:
2025 case DtdInputState.InsideSingleQuoted:
2028 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2032 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2034 if (State == DtdInputState.InsideDoubleQuoted ||
2035 State == DtdInputState.InsideSingleQuoted)
2036 continue; // well-formed
2037 switch (ReadChar ()) {
2039 stateStack.Push (DtdInputState.PI);
2042 switch (ReadChar ()) {
2044 switch (ReadChar ()) {
2047 stateStack.Push (DtdInputState.ElementDecl);
2051 stateStack.Push (DtdInputState.EntityDecl);
2054 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2059 stateStack.Push (DtdInputState.AttlistDecl);
2063 stateStack.Push (DtdInputState.NotationDecl);
2067 stateStack.Push (DtdInputState.Comment);
2072 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2076 if (State == DtdInputState.InsideSingleQuoted)
2078 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2079 stateStack.Push (DtdInputState.InsideSingleQuoted);
2082 if (State == DtdInputState.InsideDoubleQuoted)
2084 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2085 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2089 case DtdInputState.ElementDecl:
2090 goto case DtdInputState.NotationDecl;
2091 case DtdInputState.AttlistDecl:
2092 goto case DtdInputState.NotationDecl;
2093 case DtdInputState.EntityDecl:
2094 goto case DtdInputState.NotationDecl;
2095 case DtdInputState.NotationDecl:
2098 case DtdInputState.InsideDoubleQuoted:
2100 case DtdInputState.InsideSingleQuoted:
2101 continue; // well-formed
2102 case DtdInputState.Comment:
2105 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2109 if (State == DtdInputState.PI) {
2110 if (ReadChar () == '>')
2115 if (State == DtdInputState.Comment) {
2116 if (PeekChar () == '-') {
2124 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2125 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2131 // Read any one of following:
2132 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
2133 // PI, Comment, Parameter Entity, or doctype termination char(']')
2135 // Returns true if it may have any more contents, or false if not.
2136 private bool CompileDTDSubset()
2144 // It affects on entity references' well-formedness
2145 if (this.parserInputStack.Count == 0)
2146 DTD.InternalSubsetHasPEReference = true;
2148 string peName = ReadName ();
2150 currentInput.InsertParameterEntityBuffer (GetPEValue (peName));
2151 int currentLine = currentInput.LineNumber;
2152 int currentColumn = currentInput.LinePosition;
2153 while (currentInput.HasPEBuffer)
2154 CompileDTDSubset ();
2155 if (currentInput.LineNumber != currentLine ||
2156 currentInput.LinePosition != currentColumn)
2157 throw new XmlException (this as IXmlLineInfo,
2158 "Incorrectly nested parameter entity.");
2165 // Only read, no store.
2166 ReadProcessingInstruction ();
2169 CompileDeclaration ();
2172 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<' character.");
2176 if (dtdIncludeSect == 0)
2177 throw new XmlException (this as IXmlLineInfo, "Unbalanced end of INCLUDE/IGNORE section.");
2184 throw new XmlException (this as IXmlLineInfo,String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
2189 private void CompileDeclaration ()
2195 // Only read, no store.
2203 if (!SkipWhitespace ())
2204 throw new XmlException (this as IXmlLineInfo,
2205 "Whitespace is required after '<!ENTITY' in DTD entity declaration.");
2207 if (PeekChar () == '%') {
2209 if (!SkipWhitespace ()) {
2215 if (XmlChar.IsNameChar (PeekChar ()))
2216 ReadParameterEntityDecl ();
2218 throw new XmlException (this as IXmlLineInfo,"expected name character");
2222 DTDEntityDeclaration ent = ReadEntityDecl ();
2223 if (DTD.EntityDecls [ent.Name] == null)
2224 DTD.EntityDecls.Add (ent.Name, ent);
2228 DTDElementDeclaration el = ReadElementDecl ();
2229 DTD.ElementDecls.Add (el.Name, el);
2232 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
2237 DTDAttListDeclaration atl = ReadAttListDecl ();
2238 // if (DTD.AttListDecls.ContainsKey (atl.Name))
2239 DTD.AttListDecls.Add (atl.Name, atl);
2243 DTDNotationDeclaration not = ReadNotationDecl ();
2244 DTD.NotationDecls.Add (not.Name, not);
2247 // conditional sections
2252 switch (ReadChar ()) {
2266 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!' characters.");
2270 private void ReadIgnoreSect ()
2275 int dtdIgnoreSect = 1;
2276 while (dtdIgnoreSect > 0) {
2277 switch (skip ? PeekChar () : ReadChar ()) {
2279 throw new XmlException (this as IXmlLineInfo,"Unexpected IGNORE section end.");
2281 if (ReadChar () == '!' && ReadChar () == '[')
2285 if (ReadChar () == ']') {
2286 if (ReadChar () == '>')
2297 // The reader is positioned on the head of the name.
2298 private DTDElementDeclaration ReadElementDecl ()
2300 DTDElementDeclaration decl = new DTDElementDeclaration (DTD);
2301 if (!SkipWhitespace ())
2302 throw new XmlException (this as IXmlLineInfo,
2303 "Whitespace is required between '<!ELEMENT' and name in DTD element declaration.");
2306 decl.Name = ReadName ();
2307 if (!SkipWhitespace ())
2308 throw new XmlException (this as IXmlLineInfo,
2309 "Whitespace is required between name and content in DTD element declaration.");
2311 ReadContentSpec (decl);
2313 // This expanding is only allowed as a non-validating parser.
2320 // read 'children'(BNF) of contentspec
2321 private void ReadContentSpec (DTDElementDeclaration decl)
2328 decl.IsEmpty = true;
2336 DTDContentModel model = decl.ContentModel;
2341 if(PeekChar () == '#') {
2342 // Mixed Contents. "#PCDATA" must appear first.
2343 decl.IsMixedContent = true;
2344 model.Occurence = DTDOccurence.ZeroOrMore;
2345 model.OrderType = DTDContentOrderType.Or;
2350 while(PeekChar () != ')') {
2355 DTDContentModel elem = new DTDContentModel (DTD, decl.Name);
2356 elem.ElementName = ReadName ();
2357 model.ChildModels.Add (elem);
2363 if (model.ChildModels.Count > 0)
2365 else if (PeekChar () == '*')
2368 // Non-Mixed Contents
2369 model.ChildModels.Add (ReadCP (decl));
2372 do { // copied from ReadCP() ...;-)
2375 if(PeekChar ()=='|') {
2377 if (model.OrderType == DTDContentOrderType.Seq)
2378 throw new XmlException (this as IXmlLineInfo,
2379 "Inconsistent choice markup in sequence cp.");
2380 model.OrderType = DTDContentOrderType.Or;
2383 model.ChildModels.Add (ReadCP (decl));
2386 else if(PeekChar () == ',')
2389 if (model.OrderType == DTDContentOrderType.Or)
2390 throw new XmlException (this as IXmlLineInfo,
2391 "Inconsistent sequence markup in choice cp.");
2392 model.OrderType = DTDContentOrderType.Seq;
2395 model.ChildModels.Add (ReadCP (decl));
2407 model.Occurence = DTDOccurence.Optional;
2411 model.Occurence = DTDOccurence.ZeroOrMore;
2415 model.Occurence = DTDOccurence.OneOrMore;
2426 // Read 'cp' (BNF) of contentdecl (BNF)
2427 private DTDContentModel ReadCP (DTDElementDeclaration elem)
2429 DTDContentModel model = null;
2432 if(PeekChar () == '(') {
2433 model = new DTDContentModel (DTD, elem.Name);
2436 model.ChildModels.Add (ReadCP (elem));
2441 if(PeekChar ()=='|') {
2443 if (model.OrderType == DTDContentOrderType.Seq)
2444 throw new XmlException (this as IXmlLineInfo,
2445 "Inconsistent choice markup in sequence cp.");
2446 model.OrderType = DTDContentOrderType.Or;
2449 model.ChildModels.Add (ReadCP (elem));
2452 else if(PeekChar () == ',') {
2454 if (model.OrderType == DTDContentOrderType.Or)
2455 throw new XmlException (this as IXmlLineInfo,
2456 "Inconsistent sequence markup in choice cp.");
2457 model.OrderType = DTDContentOrderType.Seq;
2460 model.ChildModels.Add (ReadCP (elem));
2472 model = new DTDContentModel (DTD, elem.Name);
2474 model.ElementName = ReadName ();
2477 switch(PeekChar ()) {
2479 model.Occurence = DTDOccurence.Optional;
2483 model.Occurence = DTDOccurence.ZeroOrMore;
2487 model.Occurence = DTDOccurence.OneOrMore;
2494 // The reader is positioned on the first name char.
2495 private void ReadParameterEntityDecl ()
2497 DTDParameterEntityDeclaration decl =
2498 new DTDParameterEntityDeclaration();
2499 decl.BaseURI = BaseURI;
2501 decl.Name = ReadName ();
2502 if (!SkipWhitespace ())
2503 throw new XmlException (this as IXmlLineInfo,
2504 "Whitespace is required after name in DTD parameter entity declaration.");
2506 if (PeekChar () == 'S' || PeekChar () == 'P') {
2507 // throw new NotImplementedException ("External parameter entity reference is not implemented yet.");
2508 // read publicId/systemId
2510 decl.PublicId = GetAttribute ("PUBLIC");
2511 decl.SystemId = GetAttribute ("SYSTEM");
2513 decl.Resolve (resolver);
2517 int quoteChar = ReadChar ();
2518 int start = currentTag.Length;
2519 ClearValueBuffer ();
2522 int c = PeekChar ();
2525 throw new XmlException ("unexpected end of stream in entity value definition.");
2528 if (quoteChar == '"')
2531 AppendValueChar ('"');
2535 if (quoteChar == '\'')
2538 AppendValueChar ('\'');
2542 if (PeekChar () == '#') {
2544 ReadCharacterReference ();
2547 AppendValueChar ('&');
2551 string peName = ReadName ();
2553 valueBuffer.Append (GetPEValue (peName));
2556 AppendValueChar (ReadChar ());
2560 decl.LiteralValue = CreateValueString (); // currentTag.ToString (start, currentTag.Length - start - 1);
2561 ClearValueBuffer ();
2565 if (parameterEntities [decl.Name] == null) {
2566 parameterEntities.Add (decl.Name, decl);
2570 private string GetPEValue (string peName)
2572 DTDParameterEntityDeclaration peDecl =
2573 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
2575 return peDecl.Value;
2576 // See XML 1.0 section 4.1 for both WFC and VC.
2577 if ((DTD.SystemId == null && !DTD.InternalSubsetHasPEReference) || this.isStandalone)
2578 throw new XmlException (this as IXmlLineInfo,
2579 "Parameter entity " + peName + " not found.");
2580 DTD.AddError (new XmlSchemaException (
2581 "Parameter entity " + peName + " not found.", null));
2585 private void TryExpandPERef ()
2587 if (PeekChar () == '%') {
2589 // if (!XmlChar.IsNameChar (PeekChar ()))
2596 // reader is positioned on '%'
2597 private void ImportAsPERef ()
2600 string peName = ReadName ();
2602 DTDParameterEntityDeclaration peDecl =
2603 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
2604 if (peDecl == null) {
2605 DTD.AddError (new XmlSchemaException ("Parameter entity " + peName + " not found.", null));
2606 return; // do nothing
2608 currentInput.InsertParameterEntityBuffer (" " + peDecl.Value + " ");
2612 // The reader is positioned on the head of the name.
2613 private DTDEntityDeclaration ReadEntityDecl ()
2615 DTDEntityDeclaration decl = new DTDEntityDeclaration (DTD);
2616 decl.IsInternalSubset = (parserInputStack.Count == 0);
2619 decl.Name = ReadName ();
2620 if (!SkipWhitespace ())
2621 throw new XmlException (this as IXmlLineInfo,
2622 "Whitespace is required between name and content in DTD entity declaration.");
2626 if (PeekChar () == 'S' || PeekChar () == 'P') {
2629 decl.PublicId = GetAttribute ("PUBLIC");
2630 decl.SystemId = GetAttribute ("SYSTEM");
2631 if (SkipWhitespace ()) {
2632 if (PeekChar () == 'N') {
2635 if (!SkipWhitespace ())
2636 throw new XmlException (this as IXmlLineInfo,
2637 "Whitespace is required after NDATA.");
2638 decl.NotationName = ReadName (); // ndata_name
2641 decl.ScanEntityValue (new StringCollection ());
2645 ReadEntityValueDecl (decl);
2648 // This expanding is only allowed as a non-validating parser.
2655 private void ReadEntityValueDecl (DTDEntityDeclaration decl)
2658 // quotation char will be finally removed on unescaping
2659 int quoteChar = ReadChar ();
2660 int start = currentTag.Length;
2661 if (quoteChar != '\'' && quoteChar != '"')
2662 throw new XmlException ("quotation char was expected.");
2664 ClearValueBuffer ();
2666 while (PeekChar () != quoteChar) {
2667 switch (PeekChar ()) {
2670 string name = ReadName ();
2672 if (decl.IsInternalSubset)
2673 throw new XmlException (this as IXmlLineInfo,
2674 "Parameter entity is not allowed in internal subset entity '" + name + "'");
2675 valueBuffer.Append (GetPEValue (name));
2678 throw new XmlException ("unexpected end of stream.");
2680 AppendValueChar (ReadChar ());
2684 string value = Dereference (CreateValueString (), false);
2685 ClearValueBuffer ();
2688 decl.LiteralEntityValue = value;
2691 private DTDAttListDeclaration ReadAttListDecl ()
2696 string name = ReadName (); // target element name
2697 DTDAttListDeclaration decl =
2698 DTD.AttListDecls [name] as DTDAttListDeclaration;
2700 decl = new DTDAttListDeclaration (DTD);
2703 if (!SkipWhitespace ())
2704 if (PeekChar () != '>')
2705 throw new XmlException (this as IXmlLineInfo,
2706 "Whitespace is required between name and content in non-empty DTD attlist declaration.");
2711 while (XmlChar.IsNameChar ((char) PeekChar ())) {
2712 DTDAttributeDefinition def = ReadAttributeDefinition ();
2713 if (decl [def.Name] == null)
2720 // This expanding is only allowed as a non-validating parser.
2727 private DTDAttributeDefinition ReadAttributeDefinition ()
2729 DTDAttributeDefinition def = new DTDAttributeDefinition ();
2734 def.Name = ReadName ();
2735 if (!SkipWhitespace ())
2736 throw new XmlException (this as IXmlLineInfo,
2737 "Whitespace is required between name and content in DTD attribute definition.");
2742 switch(PeekChar ()) {
2745 def.Datatype = XmlSchemaDatatype.FromName ("normalizedString");
2747 case 'I': // ID, IDREF, IDREFS
2749 if(PeekChar () == 'R') {
2751 if(PeekChar () == 'S') {
2754 def.Datatype = XmlSchemaDatatype.FromName ("IDREFS");
2757 def.Datatype = XmlSchemaDatatype.FromName ("IDREF");
2760 def.Datatype = XmlSchemaDatatype.FromName ("ID");
2762 case 'E': // ENTITY, ENTITIES
2764 switch(ReadChar ()) {
2766 def.Datatype = XmlSchemaDatatype.FromName ("ENTITY");
2768 case 'I': // ENTITIES
2770 def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES");
2774 case 'N': // NMTOKEN, NMTOKENS, NOTATION
2776 switch(PeekChar ()) {
2779 if(PeekChar ()=='S') { // NMTOKENS
2781 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS");
2784 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN");
2788 def.Datatype = XmlSchemaDatatype.FromName ("NOTATION");
2789 if (!SkipWhitespace ())
2790 throw new XmlException (this as IXmlLineInfo,
2791 "Whitespace is required between name and content in DTD attribute definition.");
2794 def.EnumeratedNotations.Add (ReadName ()); // notation name
2796 while(PeekChar () == '|') {
2799 def.EnumeratedNotations.Add (ReadName ()); // notation name
2805 throw new XmlException ("attribute declaration syntax error.");
2808 default: // Enumerated Values
2809 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN");
2814 def.EnumeratedAttributeDeclaration.Add (
2815 def.Datatype.Normalize (ReadNmToken ())); // enum value
2817 while(PeekChar () == '|') {
2820 def.EnumeratedAttributeDeclaration.Add (
2821 def.Datatype.Normalize (ReadNmToken ())); // enum value
2828 if (!SkipWhitespace ())
2829 throw new XmlException (this as IXmlLineInfo,
2830 "Whitespace is required between type and occurence in DTD attribute definition.");
2833 if(PeekChar () == '#')
2839 Expect ("REQUIRED");
2840 def.OccurenceType = DTDAttributeOccurenceType.Required;
2844 def.OccurenceType = DTDAttributeOccurenceType.Optional;
2848 def.OccurenceType = DTDAttributeOccurenceType.Fixed;
2849 if (!SkipWhitespace ())
2850 throw new XmlException (this as IXmlLineInfo,
2851 "Whitespace is required between FIXED and actual value in DTD attribute definition.");
2852 def.UnresolvedDefaultValue = ReadAttribute (true);
2856 // one of the enumerated value
2859 def.UnresolvedDefaultValue = ReadAttribute (true);
2865 private DTDNotationDeclaration ReadNotationDecl()
2867 DTDNotationDeclaration decl = new DTDNotationDeclaration ();
2870 decl.Name = ReadName (); // notation name
2871 if (namespaces) { // copy from SetProperties ;-)
2872 int indexOfColon = decl.Name.IndexOf (':');
2874 if (indexOfColon == -1) {
2875 decl.Prefix = String.Empty;
2876 decl.LocalName = decl.Name;
2878 decl.Prefix = decl.Name.Substring (0, indexOfColon);
2879 decl.LocalName = decl.Name.Substring (indexOfColon + 1);
2882 decl.Prefix = String.Empty;
2883 decl.LocalName = decl.Name;
2887 if(PeekChar () == 'P') {
2888 decl.PublicId = ReadPubidLiteral ();
2889 bool wsSkipped = SkipWhitespace ();
2890 if (PeekChar () == '\'' || PeekChar () == '"') {
2892 throw new XmlException (this as IXmlLineInfo,
2893 "Whitespace is required between public id and system id.");
2894 decl.SystemId = ReadSystemLiteral (false);
2897 } else if(PeekChar () == 'S') {
2898 decl.SystemId = ReadSystemLiteral (true);
2901 if(decl.PublicId == null && decl.SystemId == null)
2902 throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
2903 // This expanding is only allowed as a non-validating parser.
2910 private void ReadExternalID () {
2911 this.ClearAttributes ();
2912 switch (PeekChar ()) {
2914 string systemId = ReadSystemLiteral (true);
2915 AddAttribute ("SYSTEM", systemId);
2918 string publicId = ReadPubidLiteral ();
2919 if (!SkipWhitespace ())
2920 throw new XmlException (this as IXmlLineInfo,
2921 "Whitespace is required between PUBLIC id and SYSTEM id.");
2922 systemId = ReadSystemLiteral (false);
2923 AddAttribute ("PUBLIC", publicId);
2924 AddAttribute ("SYSTEM", systemId);
2929 // The reader is positioned on the first 'S' of "SYSTEM".
2930 private string ReadSystemLiteral (bool expectSYSTEM)
2934 if (!SkipWhitespace ())
2935 throw new XmlException (this as IXmlLineInfo,
2936 "Whitespace is required after 'SYSTEM'.");
2940 int quoteChar = ReadChar (); // apos or quot
2941 int startPos = currentTag.Length;
2943 ClearValueBuffer ();
2944 while (c != quoteChar) {
2947 throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2949 AppendValueChar (c);
2951 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2954 private string ReadPubidLiteral()
2957 if (!SkipWhitespace ())
2958 throw new XmlException (this as IXmlLineInfo,
2959 "Whitespace is required after 'PUBLIC'.");
2960 int quoteChar = ReadChar ();
2961 int startPos = currentTag.Length;
2963 ClearValueBuffer ();
2964 while(c != quoteChar)
2967 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2968 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2969 throw new XmlException (this as IXmlLineInfo,"character '" + (char)c + "' not allowed for PUBLIC ID");
2971 AppendValueChar (c);
2973 return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2976 // The reader is positioned on the first character
2978 internal string ReadName ()
2980 return ReadNameOrNmToken(false);
2983 // The reader is positioned on the first character
2985 private string ReadNmToken ()
2987 return ReadNameOrNmToken(true);
2990 private string ReadNameOrNmToken(bool isNameToken)
2992 int ch = PeekChar ();
2994 if (!XmlChar.IsNameChar ((char) ch))
2995 throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char)ch));
2998 if (!XmlChar.IsFirstNameChar (ch))
2999 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
3004 AppendNameChar (ReadChar ());
3006 while (XmlChar.IsNameChar (PeekChar ())) {
3007 AppendNameChar (ReadChar ());
3010 return CreateNameString ();
3013 // Read the next character and compare it against the
3014 // specified character.
3015 private void Expect (int expected)
3017 int ch = ReadChar ();
3019 if (ch != expected) {
3020 throw new XmlException (this as IXmlLineInfo,
3022 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
3030 private void Expect (string expected)
3032 int len = expected.Length;
3033 for(int i=0; i< len; i++)
3034 Expect (expected[i]);
3037 // Does not consume the first non-whitespace character.
3038 private bool SkipWhitespace ()
3040 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
3041 bool skipped = XmlChar.IsWhitespace (PeekChar ());
3042 while (XmlChar.IsWhitespace (PeekChar ()))
3047 private void ReadWhitespace ()
3049 if (currentState == XmlNodeType.None)
3050 currentState = XmlNodeType.XmlDeclaration;
3052 ClearValueBuffer ();
3053 int ch = PeekChar ();
3055 AppendValueChar (ReadChar ());
3056 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
3058 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
3061 SetProperties (XmlNodeType.Whitespace,
3067 return; // (PeekChar () != -1);
3070 private string Dereference (string unresolved, bool expandPredefined)
3072 StringBuilder resolved = new StringBuilder();
3074 int next = unresolved.IndexOf ('&');
3080 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
3081 int endPos = unresolved.IndexOf (';', next+1);
3083 unresolved.Substring (next + 1, endPos - next - 1);
3084 if(entityName [0] == '#') {
3087 if(entityName [1] == 'x') {
3089 c = (char) int.Parse ("0" + entityName.Substring (2),
3090 System.Globalization.NumberStyles.HexNumber);
3093 c = (char) int.Parse (entityName.Substring (1));
3095 resolved.Append (c);
3097 char predefined = XmlChar.GetPredefinedEntity (entityName);
3098 if (expandPredefined && predefined != 0)
3099 resolved.Append (predefined);
3101 // With respect to "Value", MS document is helpless
3102 // and the implemention returns inconsistent value
3103 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
3104 resolved.Append ("&" + entityName + ";");
3107 if(pos > unresolved.Length)
3109 next = unresolved.IndexOf('&', pos);
3111 resolved.Append (unresolved.Substring(pos));
3113 return resolved.ToString();