2 // System.Xml.XmlTextReader
5 // Jason Diamond (jason@injektilo.org)
6 // Adam Treat (manyoso@yahoo.com)
8 // (C) 2001, 2002 Jason Diamond http://injektilo.org/
12 // This can only parse basic XML: elements, attributes, processing
13 // instructions, and comments are OK.
15 // It barfs on DOCTYPE declarations.
16 // => No barfing, but parsing is incomplete.
17 // DTD nodes are not still created.
19 // There's also no checking being done for either well-formedness
22 // NameTables aren't being used everywhere yet.
24 // Some thought needs to be given to performance. There's too many
25 // strings being allocated.
27 // Some of the MoveTo methods haven't been implemented yet.
29 // LineNumber and LinePosition aren't being tracked.
31 // xml:space, xml:lang, and xml:base aren't being tracked.
35 using System.Collections;
41 public class XmlTextReader : XmlReader, IXmlLineInfo
43 WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
46 protected XmlTextReader ()
50 public XmlTextReader (Stream input)
51 : this (new StreamReader (input))
55 public XmlTextReader (string url)
56 : this(url, new NameTable ())
60 public XmlTextReader (TextReader input)
61 : this (input, new NameTable ())
65 protected XmlTextReader (XmlNameTable nt)
66 : this (String.Empty, null, XmlNodeType.None, null)
70 public XmlTextReader (Stream input, XmlNameTable nt)
71 : this(new StreamReader (input), nt)
75 public XmlTextReader (string url, Stream input)
76 : this (url, new StreamReader (input))
80 public XmlTextReader (string url, TextReader input)
81 : this (url, input, new NameTable ())
85 [MonoTODO("Non-filename-url must be supported. Waiting for WebClient")]
86 public XmlTextReader (string url, XmlNameTable nt)
87 // : this(url, new StreamReader ((Stream)new XmlUrlResolver ().GetEntity (new Uri (url), null, typeof(Stream))), nt)
88 : this (url, new StreamReader (url), nt)
92 public XmlTextReader (TextReader input, XmlNameTable nt)
93 : this(String.Empty, input, nt)
97 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
98 : this (String.Empty, new StreamReader (xmlFragment), fragType, context)
102 public XmlTextReader (string url, Stream input, XmlNameTable nt)
103 : this (url, new StreamReader (input), nt)
107 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
108 : this (url, input, XmlNodeType.Document, new XmlParserContext (nt, new XmlNamespaceManager (nt), null, XmlSpace.None))
112 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
113 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
114 : this (String.Empty, new StringReader (xmlFragment), fragType, context)
118 // TODO still remains as described at head of this file,
119 // but it might not be TODO of the constructors...
120 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
122 this.SetReaderContext(url, context);
123 this.SetReaderFragment(fragment, fragType);
130 public override int AttributeCount
132 get { return attributes.Count; }
135 public override string BaseURI
137 get { return parserContext.BaseURI; }
140 public override int Depth
147 public Encoding Encoding
149 get { return parserContext.Encoding; }
152 public override bool EOF
157 readState == ReadState.EndOfFile ||
158 readState == ReadState.Closed;
162 public override bool HasValue
164 get { return value != String.Empty; }
167 public override bool IsDefault
171 // XmlTextReader does not expand default attributes.
176 public override bool IsEmptyElement
178 get { return isEmptyElement; }
181 public override string this [int i]
183 get { return GetAttribute (i); }
186 public override string this [string name]
188 get { return GetAttribute (name); }
191 public override string this [string localName, string namespaceName]
193 get { return GetAttribute (localName, namespaceName); }
196 public int LineNumber
201 public int LinePosition
203 get { return column; }
206 public override string LocalName
208 get { return localName; }
211 public override string Name
217 public bool Namespaces
219 get { throw new NotImplementedException (); }
220 set { throw new NotImplementedException (); }
223 public override string NamespaceURI
225 get { return namespaceURI; }
228 public override XmlNameTable NameTable
230 get { return parserContext.NameTable; }
233 public override XmlNodeType NodeType
235 get { return nodeType; }
239 public bool Normalization
241 get { throw new NotImplementedException (); }
242 set { throw new NotImplementedException (); }
245 public override string Prefix
247 get { return prefix; }
250 public override char QuoteChar
253 // value string holds attribute quotation char.
254 if (NodeType == XmlNodeType.Attribute)
261 public override ReadState ReadState
263 get { return readState; }
266 public override string Value
269 if(NodeType == XmlNodeType.Attribute)
270 return ResolveAttributeValue(value);
276 public WhitespaceHandling WhitespaceHandling
278 get { return whitespaceHandling; }
279 set { whitespaceHandling = value; }
283 public override string XmlLang
285 get { throw new NotImplementedException (); }
289 public XmlResolver XmlResolver
291 set { throw new NotImplementedException (); }
295 public override XmlSpace XmlSpace
297 get { throw new NotImplementedException (); }
305 public override void Close ()
307 readState = ReadState.Closed;
311 public override string GetAttribute (int i)
313 if (i > attributes.Count)
314 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
316 return ResolveAttributeValue (attributes [orderedAttributes [i]] as string);
319 public override string GetAttribute (string name)
321 return attributes.ContainsKey (name) ?
322 ResolveAttributeValue (attributes [name] as string) : String.Empty;
325 public override string GetAttribute (string localName, string namespaceURI)
327 foreach (DictionaryEntry entry in attributes)
329 string thisName = entry.Key as string;
331 int indexOfColon = thisName.IndexOf (':');
333 if (indexOfColon != -1) {
334 string thisLocalName = thisName.Substring (indexOfColon + 1);
336 if (localName == thisLocalName) {
337 string thisPrefix = thisName.Substring (0, indexOfColon);
338 string thisNamespaceURI = LookupNamespace (thisPrefix);
340 if (namespaceURI == thisNamespaceURI)
341 return attributes.ContainsKey (thisName) ?
342 ResolveAttributeValue (attributes [thisName] as string) : String.Empty;
344 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
345 return attributes.ContainsKey (thisName) ?
346 ResolveAttributeValue (attributes [thisName] as string) : String.Empty;
353 public TextReader GetRemainder ()
355 throw new NotImplementedException ();
359 bool IXmlLineInfo.HasLineInfo ()
364 public override string LookupNamespace (string prefix)
366 return parserContext.NamespaceManager.LookupNamespace (prefix);
369 public override void MoveToAttribute (int i)
373 if (attributes == null || orderedAttributes.Count < i || i < 0)
374 throw new ArgumentOutOfRangeException ("attribute index out of range.");
376 string name = orderedAttributes [i] as string;
377 string value = attributes [name] as string;
379 XmlNodeType.Attribute, // nodeType
381 false, // isEmptyElement
383 false // clearAttributes
387 public override bool MoveToAttribute (string name)
392 if (attributes == null)
395 if (orderedAttributesEnumerator == null) {
397 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
400 while (orderedAttributesEnumerator.MoveNext ()) {
401 if(name == orderedAttributesEnumerator.Current as string) {
408 string value = attributes [name] as string;
410 XmlNodeType.Attribute, // nodeType
412 false, // isEmptyElement
414 false // clearAttributes
422 public override bool MoveToAttribute (string localName, string namespaceName)
424 throw new NotImplementedException ();
427 public override bool MoveToElement ()
429 if (orderedAttributesEnumerator != null) {
430 orderedAttributesEnumerator = null;
431 RestoreProperties ();
438 public override bool MoveToFirstAttribute ()
441 return MoveToNextAttribute ();
444 public override bool MoveToNextAttribute ()
446 if (attributes == null)
449 if (orderedAttributesEnumerator == null) {
451 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
454 if (orderedAttributesEnumerator.MoveNext ()) {
455 string name = orderedAttributesEnumerator.Current as string;
456 string value = attributes [name] as string;
458 XmlNodeType.Attribute, // nodeType
460 false, // isEmptyElement
462 false // clearAttributes
470 public override bool Read ()
474 readState = ReadState.Interactive;
476 more = ReadContent ();
481 [MonoTODO("This method should consider entity references")]
482 public override bool ReadAttributeValue ()
484 // reading attribute value phase now stopped
485 if(attributeStringCurrentPosition < 0 ||
486 attributeString.Length < attributeStringCurrentPosition) {
487 attributeStringCurrentPosition = 0;
488 attributeString = String.Empty;
492 // If not started, then initialize attributeString when parsing is at start.
493 if(attributeStringCurrentPosition == 0)
495 value.Substring (1, value.Length - 2);
497 bool returnEntity = false;
498 value = String.Empty;
499 int nextPosition = attributeString.IndexOf ('&',
500 attributeStringCurrentPosition);
502 // if attribute string starts from '&' then it may be (unparsable) entity reference.
503 if(nextPosition == 0) {
504 string parsed = ReadAttributeValueEntityReference ();
506 // return entity (It is only this case to return entity reference.)
507 int endEntityPosition = attributeString.IndexOf (';',
508 attributeStringCurrentPosition);
509 SetProperties (XmlNodeType.EntityReference,
510 attributeString.Substring (attributeStringCurrentPosition + 1,
511 endEntityPosition - attributeStringCurrentPosition - 1),
515 attributeStringCurrentPosition = endEntityPosition + 1;
523 // Other case always set text node.
524 while(!returnEntity) {
525 nextPosition = attributeString.IndexOf ('&', attributeStringCurrentPosition);
526 if(nextPosition < 0) {
527 // Reached to the end of value string.
528 value += attributeString.Substring (attributeStringCurrentPosition);
529 attributeStringCurrentPosition = -1;
531 } else if(nextPosition == attributeStringCurrentPosition) {
532 string parsed = ReadAttributeValueEntityReference ();
536 // Found that an entity reference starts from this point.
537 // Then once stop to parse attribute value and then return text.
538 value += attributeString.Substring (attributeStringCurrentPosition,
539 nextPosition - attributeStringCurrentPosition);
543 value += attributeString.Substring (attributeStringCurrentPosition,
544 nextPosition - attributeStringCurrentPosition);
545 attributeStringCurrentPosition = nextPosition;
550 SetProperties(XmlNodeType.Text,
560 public int ReadBase64 (byte [] buffer, int offset, int length)
562 throw new NotImplementedException ();
566 public int ReadBinHex (byte [] buffer, int offset, int length)
568 throw new NotImplementedException ();
572 public int ReadChars (char [] buffer, int offset, int length)
574 throw new NotImplementedException ();
578 public override string ReadInnerXml ()
580 // Still need a Well Formedness check.
581 // Will wait for Validating reader ;-)
582 if (NodeType == XmlNodeType.Attribute) {
585 saveToXmlBuffer = true;
586 string startname = this.Name;
587 string endname = string.Empty;
588 readState = ReadState.Interactive;
590 while (startname != endname) {
595 xmlBuffer.Replace (currentTag.ToString (), "");
596 saveToXmlBuffer = false;
597 string InnerXml = xmlBuffer.ToString ();
598 xmlBuffer.Length = 0;
604 public override string ReadOuterXml ()
606 if (NodeType == XmlNodeType.Attribute) {
607 return Name + "=\"" + Value.Replace ("\"", """) + "\"";
609 saveToXmlBuffer = true;
610 xmlBuffer.Append (currentTag.ToString ());
611 int startDepth = Depth;
612 readState = ReadState.Interactive;
616 } while (Depth > startDepth);
618 saveToXmlBuffer = false;
619 string OuterXml = xmlBuffer.ToString ();
620 xmlBuffer.Length = 0;
626 public override string ReadString ()
628 throw new NotImplementedException ();
632 public void ResetState ()
634 throw new NotImplementedException ();
637 public override void ResolveEntity ()
639 // XmlTextReaders don't resolve entities.
640 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
646 internal string publicId;
647 internal string systemId;
649 internal void SetReaderContext (string url, XmlParserContext context)
651 parserContext = context;
652 parserContext.BaseURI = url;
656 internal void SetReaderFragment(TextReader fragment, XmlNodeType fragType)
658 this.reader = fragment;
659 can_seek = fragment != null && fragment.Peek () != -1;
661 if (fragType == XmlNodeType.Attribute)
666 case XmlNodeType.Attribute: // attribute content
667 parserContext.InputState = XmlParserInputState.AttributeValue;
669 case XmlNodeType.DocumentFragment: // element content
670 parserContext.InputState = XmlParserInputState.Content;
672 case XmlNodeType.Element: // one element
673 parserContext.InputState = XmlParserInputState.StartTag;
675 case XmlNodeType.Document: // document content
676 parserContext.InputState = XmlParserInputState.Start;
679 throw new InvalidOperationException("setting this xml node type not allowed.");
687 private XmlParserContext parserContext;
689 private TextReader reader;
690 private ReadState readState;
693 private int elementDepth;
694 private bool depthDown;
696 private bool popScope;
698 private XmlNodeType nodeType;
700 private string prefix;
701 private string localName;
702 private string namespaceURI;
703 private bool isEmptyElement;
704 private string value;
706 private XmlNodeType saveNodeType;
707 private string saveName;
708 private string savePrefix;
709 private string saveLocalName;
710 private string saveNamespaceURI;
711 private bool saveIsEmptyElement;
713 private Hashtable attributes;
714 private ArrayList orderedAttributes;
715 private IEnumerator orderedAttributesEnumerator;
717 private bool returnEntityReference;
718 private string entityReferenceName;
720 private char [] nameBuffer;
721 private int nameLength;
722 private int nameCapacity;
723 private const int initialNameCapacity = 256;
725 private char [] valueBuffer;
726 private int valueLength;
727 private int valueCapacity;
728 private const int initialValueCapacity = 8192;
730 private StringBuilder xmlBuffer; // This is for Read(Inner|Outer)Xml
731 private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml
732 private bool saveToXmlBuffer;
733 private int line = 1;
734 private int column = 1;
735 private bool has_peek;
736 private bool can_seek;
737 private int peek_char;
739 private string attributeString = String.Empty;
740 private int attributeStringCurrentPosition;
744 readState = ReadState.Initial;
751 nodeType = XmlNodeType.None;
753 prefix = String.Empty;
754 localName = string.Empty;
755 isEmptyElement = false;
756 value = String.Empty;
758 attributes = new Hashtable ();
759 orderedAttributes = new ArrayList ();
760 orderedAttributesEnumerator = null;
762 returnEntityReference = false;
763 entityReferenceName = String.Empty;
765 nameBuffer = new char [initialNameCapacity];
767 nameCapacity = initialNameCapacity;
769 valueBuffer = new char [initialValueCapacity];
771 valueCapacity = initialValueCapacity;
773 xmlBuffer = new StringBuilder ();
774 currentTag = new StringBuilder ();
777 // Use this method rather than setting the properties
778 // directly so that all the necessary properties can
779 // be changed in harmony with each other. Maybe the
780 // fields should be in a seperate class to help enforce
782 private void SetProperties (
783 XmlNodeType nodeType,
787 bool clearAttributes)
789 this.nodeType = nodeType;
791 this.isEmptyElement = isEmptyElement;
793 this.elementDepth = depth;
798 int indexOfColon = name.IndexOf (':');
800 if (indexOfColon == -1) {
801 prefix = String.Empty;
804 prefix = name.Substring (0, indexOfColon);
805 localName = name.Substring (indexOfColon + 1);
808 namespaceURI = LookupNamespace (prefix);
811 private void SaveProperties ()
813 saveNodeType = nodeType;
816 saveLocalName = localName;
817 saveNamespaceURI = namespaceURI;
818 saveIsEmptyElement = isEmptyElement;
819 // An element's value is always String.Empty.
822 private void RestoreProperties ()
824 nodeType = saveNodeType;
827 localName = saveLocalName;
828 namespaceURI = saveNamespaceURI;
829 isEmptyElement = saveIsEmptyElement;
830 value = String.Empty;
833 private void AddAttribute (string name, string value)
835 attributes.Add (name, value);
836 orderedAttributes.Add (name);
839 private void ClearAttributes ()
841 if (attributes.Count > 0) {
843 orderedAttributes.Clear ();
846 orderedAttributesEnumerator = null;
849 private int PeekChar ()
852 return reader.Peek ();
857 peek_char = reader.Read ();
862 private int ReadChar ()
878 if (saveToXmlBuffer) {
879 xmlBuffer.Append ((char) ch);
881 currentTag.Append ((char) ch);
885 // This should really keep track of some state so
886 // that it's not possible to have more than one document
887 // element or text outside of the document element.
888 private bool ReadContent ()
890 currentTag.Length = 0;
892 parserContext.NamespaceManager.PopScope ();
896 if (returnEntityReference) {
897 SetEntityReferenceProperties ();
906 if (whitespaceHandling == WhitespaceHandling.All ||
907 whitespaceHandling == WhitespaceHandling.Significant)
908 return ReadWhitespace ();
911 return ReadContent ();
913 if (whitespaceHandling == WhitespaceHandling.All ||
914 whitespaceHandling == WhitespaceHandling.Significant)
915 return ReadWhitespace ();
918 return ReadContent ();
920 if (whitespaceHandling == WhitespaceHandling.All ||
921 whitespaceHandling == WhitespaceHandling.Significant)
922 return ReadWhitespace ();
925 return ReadContent ();
927 readState = ReadState.EndOfFile;
929 XmlNodeType.None, // nodeType
930 String.Empty, // name
931 false, // isEmptyElement
932 String.Empty, // value
933 true // clearAttributes
941 return this.ReadState != ReadState.EndOfFile;
944 private void SetEntityReferenceProperties ()
947 XmlNodeType.EntityReference, // nodeType
948 entityReferenceName, // name
949 false, // isEmptyElement
950 String.Empty, // value
951 true // clearAttributes
954 returnEntityReference = false;
955 entityReferenceName = String.Empty;
958 // The leading '<' has already been consumed.
959 private void ReadTag ()
969 ReadProcessingInstruction ();
981 // The leading '<' has already been consumed.
982 private void ReadStartTag ()
984 parserContext.NamespaceManager.PushScope ();
986 string name = ReadName ();
989 bool isEmptyElement = false;
993 if (XmlChar.IsFirstNameChar (PeekChar ()))
996 if (PeekChar () == '/') {
998 isEmptyElement = true;
1006 XmlNodeType.Element, // nodeType
1008 isEmptyElement, // isEmptyElement
1009 String.Empty, // value
1010 false // clearAttributes
1020 // The reader is positioned on the first character
1021 // of the element's name.
1022 private void ReadEndTag ()
1024 string name = ReadName ();
1031 XmlNodeType.EndElement, // nodeType
1033 false, // isEmptyElement
1034 String.Empty, // value
1035 true // clearAttributes
1041 private void AppendNameChar (int ch)
1043 CheckNameCapacity ();
1044 nameBuffer [nameLength++] = (char)ch;
1047 private void CheckNameCapacity ()
1049 if (nameLength == nameCapacity) {
1050 nameCapacity = nameCapacity * 2;
1051 char [] oldNameBuffer = nameBuffer;
1052 nameBuffer = new char [nameCapacity];
1053 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1057 private string CreateNameString ()
1059 return new String (nameBuffer, 0, nameLength);
1062 private void AppendValueChar (int ch)
1064 CheckValueCapacity ();
1065 valueBuffer [valueLength++] = (char)ch;
1068 private void CheckValueCapacity ()
1070 if (valueLength == valueCapacity) {
1071 valueCapacity = valueCapacity * 2;
1072 char [] oldValueBuffer = valueBuffer;
1073 valueBuffer = new char [valueCapacity];
1074 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1078 private string CreateValueString ()
1080 return new String (valueBuffer, 0, valueLength);
1083 // The reader is positioned on the first character
1085 private void ReadText (bool cleanValue)
1090 int ch = PeekChar ();
1092 while (ch != '<' && ch != -1) {
1095 if (ReadReference (false))
1098 AppendValueChar (ReadChar ());
1103 if (returnEntityReference && valueLength == 0) {
1104 SetEntityReferenceProperties ();
1107 XmlNodeType.Text, // nodeType
1108 String.Empty, // name
1109 false, // isEmptyElement
1110 CreateValueString (), // value
1111 true // clearAttributes
1116 // The leading '&' has already been consumed.
1117 // Returns true if the entity reference isn't a simple
1118 // character reference or one of the predefined entities.
1119 // This allows the ReadText method to break so that the
1120 // next call to Read will return the EntityReference node.
1121 private bool ReadReference (bool ignoreEntityReferences)
1123 if (PeekChar () == '#') {
1125 ReadCharacterReference ();
1127 ReadEntityReference (ignoreEntityReferences);
1129 return returnEntityReference;
1132 private void ReadCharacterReference ()
1136 if (PeekChar () == 'x') {
1139 while (PeekChar () != ';' && PeekChar () != -1) {
1140 int ch = ReadChar ();
1142 if (ch >= '0' && ch <= '9')
1143 value = (value << 4) + ch - '0';
1144 else if (ch >= 'A' && ch <= 'F')
1145 value = (value << 4) + ch - 'A' + 10;
1146 else if (ch >= 'a' && ch <= 'f')
1147 value = (value << 4) + ch - 'a' + 10;
1149 throw new XmlException (
1151 "invalid hexadecimal digit: {0} (#x{1:X})",
1156 while (PeekChar () != ';' && PeekChar () != -1) {
1157 int ch = ReadChar ();
1159 if (ch >= '0' && ch <= '9')
1160 value = value * 10 + ch - '0';
1162 throw new XmlException (
1164 "invalid decimal digit: {0} (#x{1:X})",
1172 AppendValueChar (value);
1175 private void ReadEntityReference (bool ignoreEntityReferences)
1179 int ch = PeekChar ();
1181 while (ch != ';' && ch != -1) {
1182 AppendNameChar (ReadChar ());
1188 string name = CreateNameString ();
1193 AppendValueChar ('<');
1196 AppendValueChar ('>');
1199 AppendValueChar ('&');
1202 AppendValueChar ('\'');
1205 AppendValueChar ('"');
1208 if (ignoreEntityReferences) {
1209 AppendValueChar ('&');
1211 foreach (char ch2 in name) {
1212 AppendValueChar (ch2);
1215 AppendValueChar (';');
1217 returnEntityReference = true;
1218 entityReferenceName = name;
1224 // The reader is positioned on the first character of
1225 // the attribute name.
1226 private void ReadAttributes ()
1229 string name = ReadName ();
1233 string value = ReadAttribute ();
1236 if (name == "xmlns")
1237 parserContext.NamespaceManager.AddNamespace (String.Empty, ResolveAttributeValue (value));
1238 else if (name.StartsWith ("xmlns:"))
1239 parserContext.NamespaceManager.AddNamespace (name.Substring (6), ResolveAttributeValue (value));
1241 AddAttribute (name, value);
1242 } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1245 // The reader is positioned on the quote character.
1246 private string ReadAttribute ()
1250 int quoteChar = ReadChar ();
1252 if (quoteChar != '\'' && quoteChar != '\"')
1253 throw new XmlException ("an attribute value was not quoted");
1255 // this keeps quote char to get QuoteChar property correctly.
1256 AppendValueChar (quoteChar);
1258 while (PeekChar () != quoteChar) {
1259 int ch = ReadChar ();
1264 throw new XmlException ("attribute values cannot contain '<'");
1265 // expansion of entity now should be done at ResolveAttributeValue() method
1267 // ReadReference (true);
1270 throw new XmlException ("unexpected end of file in an attribute value");
1272 AppendValueChar (ch);
1277 ReadChar (); // quoteChar
1278 AppendValueChar (quoteChar);
1280 return CreateValueString ();
1283 // The reader is positioned on the first character
1286 // Now it also reads XmlDeclaration, this method name became improper...
1287 private void ReadProcessingInstruction ()
1289 string target = ReadName ();
1294 while (PeekChar () != -1) {
1295 int ch = ReadChar ();
1297 if (ch == '?' && PeekChar () == '>') {
1302 AppendValueChar ((char)ch);
1306 if(target == "xml") && parserContext.InputState != XmlParserInputState.Start)
1307 throw new XmlException("Xml declaration is not allowed here.");
1309 parserContext.InputState = XmlParserInputState.DTD; //for future use
1314 XmlNodeType.XmlDeclaration :
1315 XmlNodeType.ProcessingInstruction, // nodeType
1317 false, // isEmptyElement
1318 CreateValueString (), // value
1319 true // clearAttributes
1323 // The reader is positioned on the first character after
1324 // the leading '<!'.
1325 private void ReadDeclaration ()
1327 int ch = PeekChar ();
1347 // The reader is positioned on the first character after
1348 // the leading '<!--'.
1349 private void ReadComment ()
1353 while (PeekChar () != -1) {
1354 int ch = ReadChar ();
1356 if (ch == '-' && PeekChar () == '-') {
1359 if (PeekChar () != '>')
1360 throw new XmlException ("comments cannot contain '--'");
1366 AppendValueChar ((char)ch);
1370 XmlNodeType.Comment, // nodeType
1371 String.Empty, // name
1372 false, // isEmptyElement
1373 CreateValueString (), // value
1374 true // clearAttributes
1378 // The reader is positioned on the first character after
1379 // the leading '<![CDATA['.
1380 private void ReadCDATA ()
1384 while (PeekChar () != -1) {
1385 int ch = ReadChar ();
1387 if (ch == ']' && PeekChar () == ']') {
1388 ch = ReadChar (); // ']'
1390 if (PeekChar () == '>') {
1394 AppendValueChar (']');
1395 AppendValueChar (']');
1400 AppendValueChar ((char)ch);
1404 XmlNodeType.CDATA, // nodeType
1405 String.Empty, // name
1406 false, // isEmptyElement
1407 CreateValueString (), // value
1408 true // clearAttributes
1412 // The reader is positioned on the first character after
1413 // the leading '<!DOCTYPE'.
1414 private void ReadDoctypeDecl ()
1416 string doctypeName = null;
1417 string publicId = String.Empty;
1418 string systemId = String.Empty;
1421 doctypeName = ReadName ();
1423 xmlBuffer.Length = 0;
1427 systemId = ReadSystemLiteral (true);
1430 publicId = ReadPubidLiteral ();
1432 systemId = ReadSystemLiteral (false);
1438 if(PeekChar () == '[')
1440 // read markupdecl etc. or end of decl
1442 xmlBuffer.Length = 0;
1443 saveToXmlBuffer = true;
1445 ReadDTDInternalSubset ();
1446 } while(nodeType != XmlNodeType.None);
1447 xmlBuffer.Remove (xmlBuffer.Length - 1, 1); // cut off ']'
1448 saveToXmlBuffer = false;
1450 // end of DOCTYPE decl.
1454 parserContext.InternalSubset = xmlBuffer.ToString ();
1456 // set properties for <!DOCTYPE> node
1458 XmlNodeType.DocumentType, // nodeType
1459 doctypeName, // name
1460 false, // isEmptyElement
1461 parserContext.InternalSubset, // value
1462 true // clearAttributes
1466 // Read any one of following:
1467 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1468 // PI, Comment, Parameter Entity, or doctype termination char(']')
1470 // returns a node of some nodeType or null, setting nodeType.
1471 // (if None then ']' was found.)
1472 private void ReadDTDInternalSubset()
1478 nodeType = XmlNodeType.None;
1481 string peName = ReadName ();
1483 nodeType = XmlNodeType.EntityReference; // It's chating a bit;-)
1489 ReadProcessingInstruction ();
1510 throw new XmlException ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1519 ReadNotationDecl ();
1522 throw new XmlException ("Syntax Error after '<!' characters.");
1526 throw new XmlException ("Syntax Error after '<' character.");
1530 throw new XmlException ("Syntax Error inside doctypedecl markup.");
1534 // The reader is positioned on the head of the name.
1535 private void ReadElementDecl()
1537 while(ReadChar () != '>');
1540 private void ReadEntityDecl()
1542 while(ReadChar () != '>');
1545 private void ReadAttListDecl()
1547 while(ReadChar () != '>');
1550 private void ReadNotationDecl()
1552 while(ReadChar () != '>');
1555 // The reader is positioned on the first 'S' of "SYSTEM".
1556 private string ReadSystemLiteral (bool expectSYSTEM)
1561 int quoteChar = ReadChar (); // apos or quot
1562 xmlBuffer.Length = 0;
1563 saveToXmlBuffer = true;
1565 while(c != quoteChar) {
1567 if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
1569 saveToXmlBuffer = false;
1570 xmlBuffer.Remove (xmlBuffer.Length-1, 1); // cut quoteChar
1571 return xmlBuffer.ToString ();
1574 private string ReadPubidLiteral()
1578 int quoteChar = ReadChar ();
1579 xmlBuffer.Length = 0;
1580 saveToXmlBuffer = true;
1582 while(c != quoteChar)
1585 if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
1586 if(c != quoteChar && !XmlChar.IsPubidChar (c))
1587 throw new XmlException("character '" + (char)c + "' not allowed for PUBLIC ID");
1589 ReadChar(); // skips quoteChar
1590 xmlBuffer.Remove (xmlBuffer.Length-1, 1); // cut quoteChar
1591 saveToXmlBuffer = false;
1592 return xmlBuffer.ToString ();
1595 // The reader is positioned on the first character
1597 private string ReadName ()
1599 if (!XmlChar.IsFirstNameChar (PeekChar ()))
1600 throw new XmlException ("a name did not start with a legal character");
1604 AppendNameChar (ReadChar ());
1606 while (XmlChar.IsNameChar (PeekChar ())) {
1607 AppendNameChar (ReadChar ());
1610 return CreateNameString ();
1613 // Read the next character and compare it against the
1614 // specified character.
1615 private void Expect (int expected)
1617 int ch = ReadChar ();
1619 if (ch != expected) {
1620 throw new XmlException (
1622 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1630 private void Expect (string expected)
1632 int len = expected.Length;
1633 for(int i=0; i< len; i++)
1634 Expect (expected[i]);
1637 // Does not consume the first non-whitespace character.
1638 private void SkipWhitespace ()
1640 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
1641 while (XmlChar.IsWhitespace (PeekChar ()))
1645 private bool ReadWhitespace ()
1648 int ch = PeekChar ();
1650 AppendValueChar (ReadChar ());
1651 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
1653 if (ch != -1 && ch != '<')
1656 SetProperties (XmlNodeType.Whitespace,
1659 CreateValueString (),
1662 return (PeekChar () != -1);
1665 // read entity reference from attribute string and if parsable then return the value.
1666 private string ReadAttributeValueEntityReference ()
1668 int endEntityPosition = attributeString.IndexOf(';',
1669 attributeStringCurrentPosition);
1670 string entityName = attributeString.Substring (attributeStringCurrentPosition + 1,
1671 endEntityPosition - attributeStringCurrentPosition - 1);
1673 attributeStringCurrentPosition = endEntityPosition + 1;
1675 if(entityName [0] == '#') {
1678 if(entityName [1] == 'x') {
1680 c = (char) int.Parse ("0" + entityName.Substring (2),
1681 System.Globalization.NumberStyles.HexNumber);
1684 c = (char) int.Parse (entityName.Substring (1));
1686 return c.ToString();
1691 case "lt": return "<";
1692 case "gt": return ">";
1693 case "amp": return "&";
1694 case "quot": return "\"";
1695 case "apos": return "'";
1696 default: return null;
1701 private string ResolveAttributeValue (string unresolved)
1703 if(unresolved == null) return null;
1704 StringBuilder resolved = new StringBuilder();
1707 // trim start/end edge of quotation character.
1708 unresolved = unresolved.Substring (1, unresolved.Length - 2);
1710 int next = unresolved.IndexOf ('&');
1716 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
1717 int endPos = unresolved.IndexOf (';', next+1);
1719 unresolved.Substring (next + 1, endPos - next - 1);
1720 if(entityName [0] == '#') {
1723 if(entityName [1] == 'x') {
1725 c = (char) int.Parse ("0" + entityName.Substring (2),
1726 System.Globalization.NumberStyles.HexNumber);
1729 c = (char) int.Parse (entityName.Substring (1));
1731 resolved.Append (c);
1733 switch(entityName) {
1734 case "lt": resolved.Append ("<"); break;
1735 case "gt": resolved.Append (">"); break;
1736 case "amp": resolved.Append ("&"); break;
1737 case "quot": resolved.Append ("\""); break;
1738 case "apos": resolved.Append ("'"); break;
1739 // With respect to "Value", MS document is helpless
1740 // and the implemention returns inconsistent value
1741 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
1742 default: resolved.Append ("&" + entityName + ";"); break;
1746 if(pos > unresolved.Length)
1748 next = unresolved.IndexOf('&', pos);
1750 resolved.Append (unresolved.Substring(pos));
1752 return resolved.ToString();