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; }
251 public override char QuoteChar
253 get { throw new NotImplementedException (); }
256 public override ReadState ReadState
258 get { return readState; }
261 public override string Value
263 get { return value; }
266 public WhitespaceHandling WhitespaceHandling
268 get { return whitespaceHandling; }
269 set { whitespaceHandling = value; }
273 public override string XmlLang
275 get { throw new NotImplementedException (); }
279 public XmlResolver XmlResolver
281 set { throw new NotImplementedException (); }
285 public override XmlSpace XmlSpace
287 get { throw new NotImplementedException (); }
295 public override void Close ()
297 readState = ReadState.Closed;
301 public override string GetAttribute (int i)
303 if (i > attributes.Count)
304 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
306 throw new NotImplementedException ();
309 public override string GetAttribute (string name)
311 return attributes.ContainsKey (name) ?
312 attributes [name] as string : String.Empty;
315 public override string GetAttribute (string localName, string namespaceURI)
317 foreach (DictionaryEntry entry in attributes)
319 string thisName = entry.Key as string;
321 int indexOfColon = thisName.IndexOf (':');
323 if (indexOfColon != -1) {
324 string thisLocalName = thisName.Substring (indexOfColon + 1);
326 if (localName == thisLocalName) {
327 string thisPrefix = thisName.Substring (0, indexOfColon);
328 string thisNamespaceURI = LookupNamespace (thisPrefix);
330 if (namespaceURI == thisNamespaceURI)
331 return attributes.ContainsKey (thisName) ?
332 attributes [thisName] as string : String.Empty;
334 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
335 return attributes.ContainsKey (thisName) ?
336 attributes [thisName] as string : String.Empty;
343 public TextReader GetRemainder ()
345 throw new NotImplementedException ();
349 bool IXmlLineInfo.HasLineInfo ()
354 public override string LookupNamespace (string prefix)
356 return parserContext.NamespaceManager.LookupNamespace (prefix);
360 public override void MoveToAttribute (int i)
362 throw new NotImplementedException ();
365 public override bool MoveToAttribute (string name)
370 if (attributes == null)
373 if (orderedAttributesEnumerator == null) {
375 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
378 while (orderedAttributesEnumerator.MoveNext ()) {
379 if(name == orderedAttributesEnumerator.Current as string) {
387 string value = attributes [name] as string;
389 XmlNodeType.Attribute, // nodeType
391 false, // isEmptyElement
393 false // clearAttributes
401 public override bool MoveToAttribute (string localName, string namespaceName)
403 throw new NotImplementedException ();
406 public override bool MoveToElement ()
408 if (orderedAttributesEnumerator != null) {
409 orderedAttributesEnumerator = null;
410 RestoreProperties ();
417 public override bool MoveToFirstAttribute ()
420 return MoveToNextAttribute ();
423 public override bool MoveToNextAttribute ()
425 if (attributes == null)
428 if (orderedAttributesEnumerator == null) {
430 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
433 if (orderedAttributesEnumerator.MoveNext ()) {
434 string name = orderedAttributesEnumerator.Current as string;
435 string value = attributes [name] as string;
437 XmlNodeType.Attribute, // nodeType
439 false, // isEmptyElement
441 false // clearAttributes
449 public override bool Read ()
453 readState = ReadState.Interactive;
455 more = ReadContent ();
460 public override bool ReadAttributeValue ()
462 // reading attribute value phase now stopped
463 if(attributeStringCurrentPosition < 0 ||
464 attributeString.Length < attributeStringCurrentPosition) {
465 attributeStringCurrentPosition = 0;
466 attributeString = String.Empty;
470 // If not started, then initialize attributeString when parsing is at start.
471 if(attributeStringCurrentPosition == 0)
472 attributeString = value;
474 bool returnEntity = false;
475 value = String.Empty;
476 int nextPosition = attributeString.IndexOf ('&',
477 attributeStringCurrentPosition);
479 // if attribute string starts from '&' then it may be (unparsable) entity reference.
480 if(nextPosition == 0) {
481 string parsed = ReadAttributeValueEntityReference ();
483 // return entity (It is only this case to return entity reference.)
484 int endEntityPosition = attributeString.IndexOf (';',
485 attributeStringCurrentPosition);
486 SetProperties (XmlNodeType.EntityReference,
487 attributeString.Substring (attributeStringCurrentPosition + 1,
488 endEntityPosition - attributeStringCurrentPosition - 1),
492 attributeStringCurrentPosition = endEntityPosition + 1;
500 // Other case always set text node.
501 while(!returnEntity) {
502 nextPosition = attributeString.IndexOf ('&', attributeStringCurrentPosition);
503 if(nextPosition < 0) {
504 // Reached to the end of value string.
505 value += attributeString.Substring (attributeStringCurrentPosition);
506 attributeStringCurrentPosition = -1;
508 } else if(nextPosition == attributeStringCurrentPosition) {
509 string parsed = ReadAttributeValueEntityReference ();
513 // Found that an entity reference starts from this point.
514 // Then once stop to parse attribute value and then return text.
515 value += attributeString.Substring (attributeStringCurrentPosition,
516 nextPosition - attributeStringCurrentPosition);
520 value += attributeString.Substring (attributeStringCurrentPosition,
521 nextPosition - attributeStringCurrentPosition);
522 attributeStringCurrentPosition = nextPosition;
527 SetProperties(XmlNodeType.Text,
537 public int ReadBase64 (byte [] buffer, int offset, int length)
539 throw new NotImplementedException ();
543 public int ReadBinHex (byte [] buffer, int offset, int length)
545 throw new NotImplementedException ();
549 public int ReadChars (char [] buffer, int offset, int length)
551 throw new NotImplementedException ();
555 public override string ReadInnerXml ()
557 // Still need a Well Formedness check.
558 // Will wait for Validating reader ;-)
559 if (NodeType == XmlNodeType.Attribute) {
562 saveToXmlBuffer = true;
563 string startname = this.Name;
564 string endname = string.Empty;
565 readState = ReadState.Interactive;
567 while (startname != endname) {
572 xmlBuffer.Replace (currentTag.ToString (), "");
573 saveToXmlBuffer = false;
574 string InnerXml = xmlBuffer.ToString ();
575 xmlBuffer.Length = 0;
581 public override string ReadOuterXml ()
583 if (NodeType == XmlNodeType.Attribute) {
584 return Name + "=\"" + Value + "\"";
586 saveToXmlBuffer = true;
587 xmlBuffer.Append (currentTag.ToString ());
588 string startname = this.Name;
589 string endname = string.Empty;
590 readState = ReadState.Interactive;
592 while (startname != endname) {
596 saveToXmlBuffer = false;
597 string OuterXml = xmlBuffer.ToString ();
598 xmlBuffer.Length = 0;
604 public override string ReadString ()
606 throw new NotImplementedException ();
610 public void ResetState ()
612 throw new NotImplementedException ();
615 public override void ResolveEntity ()
617 // XmlTextReaders don't resolve entities.
618 throw new InvalidOperationException ("XmlTextReaders don't resolve entities.");
624 internal string publicId;
625 internal string systemId;
627 internal void SetReaderContext (string url, XmlParserContext context)
629 parserContext = context;
630 parserContext.BaseURI = url;
634 internal void SetReaderFragment(TextReader fragment, XmlNodeType fragType)
636 this.reader = fragment;
637 can_seek = fragment != null && fragment.Peek () != -1;
641 case XmlNodeType.Attribute: // attribute content
642 parserContext.InputState = XmlParserInputState.AttributeValue;
644 case XmlNodeType.DocumentFragment: // element content
645 parserContext.InputState = XmlParserInputState.Content;
647 case XmlNodeType.Element: // one element
648 parserContext.InputState = XmlParserInputState.StartTag;
650 case XmlNodeType.Document: // document content
651 parserContext.InputState = XmlParserInputState.Start;
654 throw new InvalidOperationException("setting this xml node type not allowed.");
662 private XmlParserContext parserContext;
664 private TextReader reader;
665 private ReadState readState;
668 private int elementDepth;
669 private bool depthDown;
671 private bool popScope;
673 private XmlNodeType nodeType;
675 private string prefix;
676 private string localName;
677 private string namespaceURI;
678 private bool isEmptyElement;
679 private string value;
681 private XmlNodeType saveNodeType;
682 private string saveName;
683 private string savePrefix;
684 private string saveLocalName;
685 private string saveNamespaceURI;
686 private bool saveIsEmptyElement;
688 private Hashtable attributes;
689 private ArrayList orderedAttributes;
690 private IEnumerator orderedAttributesEnumerator;
692 private bool returnEntityReference;
693 private string entityReferenceName;
695 private char [] nameBuffer;
696 private int nameLength;
697 private int nameCapacity;
698 private const int initialNameCapacity = 256;
700 private char [] valueBuffer;
701 private int valueLength;
702 private int valueCapacity;
703 private const int initialValueCapacity = 8192;
705 private StringBuilder xmlBuffer; // This is for Read(Inner|Outer)Xml
706 private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml
707 private bool saveToXmlBuffer;
708 private int line = 1;
709 private int column = 1;
710 private bool has_peek;
711 private bool can_seek;
712 private int peek_char;
714 private string attributeString = String.Empty;
715 private int attributeStringCurrentPosition;
719 readState = ReadState.Initial;
726 nodeType = XmlNodeType.None;
728 prefix = String.Empty;
729 localName = string.Empty;
730 isEmptyElement = false;
731 value = String.Empty;
733 attributes = new Hashtable ();
734 orderedAttributes = new ArrayList ();
735 orderedAttributesEnumerator = null;
737 returnEntityReference = false;
738 entityReferenceName = String.Empty;
740 nameBuffer = new char [initialNameCapacity];
742 nameCapacity = initialNameCapacity;
744 valueBuffer = new char [initialValueCapacity];
746 valueCapacity = initialValueCapacity;
748 xmlBuffer = new StringBuilder ();
749 currentTag = new StringBuilder ();
752 // Use this method rather than setting the properties
753 // directly so that all the necessary properties can
754 // be changed in harmony with each other. Maybe the
755 // fields should be in a seperate class to help enforce
757 private void SetProperties (
758 XmlNodeType nodeType,
762 bool clearAttributes)
764 this.nodeType = nodeType;
766 this.isEmptyElement = isEmptyElement;
768 this.elementDepth = depth;
773 int indexOfColon = name.IndexOf (':');
775 if (indexOfColon == -1) {
776 prefix = String.Empty;
779 prefix = name.Substring (0, indexOfColon);
780 localName = name.Substring (indexOfColon + 1);
783 namespaceURI = LookupNamespace (prefix);
786 private void SaveProperties ()
788 saveNodeType = nodeType;
791 saveLocalName = localName;
792 saveNamespaceURI = namespaceURI;
793 saveIsEmptyElement = isEmptyElement;
794 // An element's value is always String.Empty.
797 private void RestoreProperties ()
799 nodeType = saveNodeType;
802 localName = saveLocalName;
803 namespaceURI = saveNamespaceURI;
804 isEmptyElement = saveIsEmptyElement;
805 value = String.Empty;
808 private void AddAttribute (string name, string value)
810 attributes.Add (name, value);
811 orderedAttributes.Add (name);
814 private void ClearAttributes ()
816 if (attributes.Count > 0) {
818 orderedAttributes.Clear ();
821 orderedAttributesEnumerator = null;
824 private int PeekChar ()
827 return reader.Peek ();
832 peek_char = reader.Read ();
837 private int ReadChar ()
853 if (saveToXmlBuffer) {
854 xmlBuffer.Append ((char) ch);
856 currentTag.Append ((char) ch);
860 // This should really keep track of some state so
861 // that it's not possible to have more than one document
862 // element or text outside of the document element.
863 private bool ReadContent ()
865 currentTag.Length = 0;
867 parserContext.NamespaceManager.PopScope ();
871 if (returnEntityReference) {
872 SetEntityReferenceProperties ();
881 if (whitespaceHandling == WhitespaceHandling.All ||
882 whitespaceHandling == WhitespaceHandling.Significant)
883 return ReadWhitespace ();
886 return ReadContent ();
888 if (whitespaceHandling == WhitespaceHandling.All ||
889 whitespaceHandling == WhitespaceHandling.Significant)
890 return ReadWhitespace ();
893 return ReadContent ();
895 if (whitespaceHandling == WhitespaceHandling.All ||
896 whitespaceHandling == WhitespaceHandling.Significant)
897 return ReadWhitespace ();
900 return ReadContent ();
902 readState = ReadState.EndOfFile;
904 XmlNodeType.None, // nodeType
905 String.Empty, // name
906 false, // isEmptyElement
907 String.Empty, // value
908 true // clearAttributes
916 return this.ReadState != ReadState.EndOfFile;
919 private void SetEntityReferenceProperties ()
922 XmlNodeType.EntityReference, // nodeType
923 entityReferenceName, // name
924 false, // isEmptyElement
925 String.Empty, // value
926 true // clearAttributes
929 returnEntityReference = false;
930 entityReferenceName = String.Empty;
933 // The leading '<' has already been consumed.
934 private void ReadTag ()
944 ReadProcessingInstruction ();
956 // The leading '<' has already been consumed.
957 private void ReadStartTag ()
959 parserContext.NamespaceManager.PushScope ();
961 string name = ReadName ();
964 bool isEmptyElement = false;
968 if (XmlChar.IsFirstNameChar (PeekChar ()))
971 if (PeekChar () == '/') {
973 isEmptyElement = true;
981 XmlNodeType.Element, // nodeType
983 isEmptyElement, // isEmptyElement
984 String.Empty, // value
985 false // clearAttributes
995 // The reader is positioned on the first character
996 // of the element's name.
997 private void ReadEndTag ()
999 string name = ReadName ();
1006 XmlNodeType.EndElement, // nodeType
1008 false, // isEmptyElement
1009 String.Empty, // value
1010 true // clearAttributes
1016 private void AppendNameChar (int ch)
1018 CheckNameCapacity ();
1019 nameBuffer [nameLength++] = (char)ch;
1022 private void CheckNameCapacity ()
1024 if (nameLength == nameCapacity) {
1025 nameCapacity = nameCapacity * 2;
1026 char [] oldNameBuffer = nameBuffer;
1027 nameBuffer = new char [nameCapacity];
1028 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1032 private string CreateNameString ()
1034 return new String (nameBuffer, 0, nameLength);
1037 private void AppendValueChar (int ch)
1039 CheckValueCapacity ();
1040 valueBuffer [valueLength++] = (char)ch;
1043 private void CheckValueCapacity ()
1045 if (valueLength == valueCapacity) {
1046 valueCapacity = valueCapacity * 2;
1047 char [] oldValueBuffer = valueBuffer;
1048 valueBuffer = new char [valueCapacity];
1049 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1053 private string CreateValueString ()
1055 return new String (valueBuffer, 0, valueLength);
1058 // The reader is positioned on the first character
1060 private void ReadText (bool cleanValue)
1065 int ch = PeekChar ();
1067 while (ch != '<' && ch != -1) {
1070 if (ReadReference (false))
1073 AppendValueChar (ReadChar ());
1078 if (returnEntityReference && valueLength == 0) {
1079 SetEntityReferenceProperties ();
1082 XmlNodeType.Text, // nodeType
1083 String.Empty, // name
1084 false, // isEmptyElement
1085 CreateValueString (), // value
1086 true // clearAttributes
1091 // The leading '&' has already been consumed.
1092 // Returns true if the entity reference isn't a simple
1093 // character reference or one of the predefined entities.
1094 // This allows the ReadText method to break so that the
1095 // next call to Read will return the EntityReference node.
1096 private bool ReadReference (bool ignoreEntityReferences)
1098 if (PeekChar () == '#') {
1100 ReadCharacterReference ();
1102 ReadEntityReference (ignoreEntityReferences);
1104 return returnEntityReference;
1107 private void ReadCharacterReference ()
1111 if (PeekChar () == 'x') {
1114 while (PeekChar () != ';' && PeekChar () != -1) {
1115 int ch = ReadChar ();
1117 if (ch >= '0' && ch <= '9')
1118 value = (value << 4) + ch - '0';
1119 else if (ch >= 'A' && ch <= 'F')
1120 value = (value << 4) + ch - 'A' + 10;
1121 else if (ch >= 'a' && ch <= 'f')
1122 value = (value << 4) + ch - 'a' + 10;
1124 throw new XmlException (
1126 "invalid hexadecimal digit: {0} (#x{1:X})",
1131 while (PeekChar () != ';' && PeekChar () != -1) {
1132 int ch = ReadChar ();
1134 if (ch >= '0' && ch <= '9')
1135 value = value * 10 + ch - '0';
1137 throw new XmlException (
1139 "invalid decimal digit: {0} (#x{1:X})",
1147 AppendValueChar (value);
1150 private void ReadEntityReference (bool ignoreEntityReferences)
1154 int ch = PeekChar ();
1156 while (ch != ';' && ch != -1) {
1157 AppendNameChar (ReadChar ());
1163 string name = CreateNameString ();
1168 AppendValueChar ('<');
1171 AppendValueChar ('>');
1174 AppendValueChar ('&');
1177 AppendValueChar ('\'');
1180 AppendValueChar ('"');
1183 if (ignoreEntityReferences) {
1184 AppendValueChar ('&');
1186 foreach (char ch2 in name) {
1187 AppendValueChar (ch2);
1190 AppendValueChar (';');
1192 returnEntityReference = true;
1193 entityReferenceName = name;
1199 // The reader is positioned on the first character of
1200 // the attribute name.
1201 private void ReadAttributes ()
1204 string name = ReadName ();
1208 string value = ReadAttribute ();
1211 if (name == "xmlns")
1212 parserContext.NamespaceManager.AddNamespace (String.Empty, value);
1213 else if (name.StartsWith ("xmlns:"))
1214 parserContext.NamespaceManager.AddNamespace (name.Substring (6), value);
1216 AddAttribute (name, value);
1217 } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1220 // The reader is positioned on the quote character.
1221 private string ReadAttribute ()
1223 int quoteChar = ReadChar ();
1225 if (quoteChar != '\'' && quoteChar != '\"')
1226 throw new XmlException ("an attribute value was not quoted");
1230 while (PeekChar () != quoteChar) {
1231 int ch = ReadChar ();
1236 throw new XmlException ("attribute values cannot contain '<'");
1238 ReadReference (true);
1241 throw new XmlException ("unexpected end of file in an attribute value");
1243 AppendValueChar (ch);
1248 ReadChar (); // quoteChar
1250 return CreateValueString ();
1253 // The reader is positioned on the first character
1256 // Now it also reads XmlDeclaration, this method name became improper...
1257 private void ReadProcessingInstruction ()
1259 string target = ReadName ();
1264 while (PeekChar () != -1) {
1265 int ch = ReadChar ();
1267 if (ch == '?' && PeekChar () == '>') {
1272 AppendValueChar ((char)ch);
1276 if(target == "xml") && parserContext.InputState != XmlParserInputState.Start)
1277 throw new XmlException("Xml declaration is not allowed here.");
1279 parserContext.InputState = XmlParserInputState.DTD; //for future use
1284 XmlNodeType.XmlDeclaration :
1285 XmlNodeType.ProcessingInstruction, // nodeType
1287 false, // isEmptyElement
1288 CreateValueString (), // value
1289 true // clearAttributes
1293 // The reader is positioned on the first character after
1294 // the leading '<!'.
1295 private void ReadDeclaration ()
1297 int ch = PeekChar ();
1317 // The reader is positioned on the first character after
1318 // the leading '<!--'.
1319 private void ReadComment ()
1323 while (PeekChar () != -1) {
1324 int ch = ReadChar ();
1326 if (ch == '-' && PeekChar () == '-') {
1329 if (PeekChar () != '>')
1330 throw new XmlException ("comments cannot contain '--'");
1336 AppendValueChar ((char)ch);
1340 XmlNodeType.Comment, // nodeType
1341 String.Empty, // name
1342 false, // isEmptyElement
1343 CreateValueString (), // value
1344 true // clearAttributes
1348 // The reader is positioned on the first character after
1349 // the leading '<![CDATA['.
1350 private void ReadCDATA ()
1354 while (PeekChar () != -1) {
1355 int ch = ReadChar ();
1357 if (ch == ']' && PeekChar () == ']') {
1358 ch = ReadChar (); // ']'
1360 if (PeekChar () == '>') {
1364 AppendValueChar (']');
1365 AppendValueChar (']');
1370 AppendValueChar ((char)ch);
1374 XmlNodeType.CDATA, // nodeType
1375 String.Empty, // name
1376 false, // isEmptyElement
1377 CreateValueString (), // value
1378 true // clearAttributes
1382 // The reader is positioned on the first character after
1383 // the leading '<!DOCTYPE'.
1384 private void ReadDoctypeDecl ()
1386 string doctypeName = null;
1387 string publicId = String.Empty;
1388 string systemId = String.Empty;
1391 doctypeName = ReadName ();
1393 xmlBuffer.Length = 0;
1397 systemId = ReadSystemLiteral (true);
1400 publicId = ReadPubidLiteral ();
1402 systemId = ReadSystemLiteral (false);
1408 if(PeekChar () == '[')
1410 // read markupdecl etc. or end of decl
1412 xmlBuffer.Length = 0;
1413 saveToXmlBuffer = true;
1415 ReadDTDInternalSubset ();
1416 } while(nodeType != XmlNodeType.None);
1417 xmlBuffer.Remove (xmlBuffer.Length - 1, 1); // cut off ']'
1418 saveToXmlBuffer = false;
1420 // end of DOCTYPE decl.
1424 parserContext.InternalSubset = xmlBuffer.ToString ();
1426 // set properties for <!DOCTYPE> node
1428 XmlNodeType.DocumentType, // nodeType
1429 doctypeName, // name
1430 false, // isEmptyElement
1431 parserContext.InternalSubset, // value
1432 true // clearAttributes
1436 // Read any one of following:
1437 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1438 // PI, Comment, Parameter Entity, or doctype termination char(']')
1440 // returns a node of some nodeType or null, setting nodeType.
1441 // (if None then ']' was found.)
1442 private void ReadDTDInternalSubset()
1448 nodeType = XmlNodeType.None;
1451 string peName = ReadName ();
1453 nodeType = XmlNodeType.EntityReference; // It's chating a bit;-)
1459 ReadProcessingInstruction ();
1480 throw new XmlException ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1489 ReadNotationDecl ();
1492 throw new XmlException ("Syntax Error after '<!' characters.");
1496 throw new XmlException ("Syntax Error after '<' character.");
1500 throw new XmlException ("Syntax Error inside doctypedecl markup.");
1504 // The reader is positioned on the head of the name.
1505 private void ReadElementDecl()
1507 while(ReadChar () != '>');
1510 private void ReadEntityDecl()
1512 while(ReadChar () != '>');
1515 private void ReadAttListDecl()
1517 while(ReadChar () != '>');
1520 private void ReadNotationDecl()
1522 while(ReadChar () != '>');
1525 // The reader is positioned on the first 'S' of "SYSTEM".
1526 private string ReadSystemLiteral (bool expectSYSTEM)
1531 int quoteChar = ReadChar (); // apos or quot
1532 xmlBuffer.Length = 0;
1533 saveToXmlBuffer = true;
1535 while(c != quoteChar) {
1537 if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
1539 saveToXmlBuffer = false;
1540 xmlBuffer.Remove (xmlBuffer.Length-1, 1); // cut quoteChar
1541 return xmlBuffer.ToString ();
1544 private string ReadPubidLiteral()
1548 int quoteChar = ReadChar ();
1549 xmlBuffer.Length = 0;
1550 saveToXmlBuffer = true;
1552 while(c != quoteChar)
1555 if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
1556 if(c != quoteChar && !XmlChar.IsPubidChar (c))
1557 throw new XmlException("character '" + (char)c + "' not allowed for PUBLIC ID");
1559 ReadChar(); // skips quoteChar
1560 xmlBuffer.Remove (xmlBuffer.Length-1, 1); // cut quoteChar
1561 saveToXmlBuffer = false;
1562 return xmlBuffer.ToString ();
1565 // The reader is positioned on the first character
1567 private string ReadName ()
1569 if (!XmlChar.IsFirstNameChar (PeekChar ()))
1570 throw new XmlException ("a name did not start with a legal character");
1574 AppendNameChar (ReadChar ());
1576 while (XmlChar.IsNameChar (PeekChar ())) {
1577 AppendNameChar (ReadChar ());
1580 return CreateNameString ();
1583 // Read the next character and compare it against the
1584 // specified character.
1585 private void Expect (int expected)
1587 int ch = ReadChar ();
1589 if (ch != expected) {
1590 throw new XmlException (
1592 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1600 private void Expect (string expected)
1602 int len = expected.Length;
1603 for(int i=0; i< len; i++)
1604 Expect (expected[i]);
1607 // Does not consume the first non-whitespace character.
1608 private void SkipWhitespace ()
1610 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
1611 while (XmlChar.IsWhitespace (PeekChar ()))
1615 private bool ReadWhitespace ()
1618 int ch = PeekChar ();
1620 AppendValueChar (ReadChar ());
1621 } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
1623 if (ch != -1 && ch != '<')
1626 SetProperties (XmlNodeType.Whitespace,
1629 CreateValueString (),
1632 return (PeekChar () != -1);
1635 // read entity reference from attribute string and if parsable then return the value.
1636 private string ReadAttributeValueEntityReference ()
1638 int endEntityPosition = attributeString.IndexOf(';',
1639 attributeStringCurrentPosition);
1640 string entityName = attributeString.Substring (attributeStringCurrentPosition + 1,
1641 endEntityPosition - attributeStringCurrentPosition - 1);
1643 attributeStringCurrentPosition = endEntityPosition + 1;
1645 if(entityName [0] == '#') {
1648 if(entityName [1] == 'x') {
1650 c = (char) int.Parse ("0" + entityName.Substring (2),
1651 System.Globalization.NumberStyles.HexNumber);
1654 c = (char) int.Parse (entityName.Substring (1));
1656 return c.ToString();
1661 case "lt": return "<";
1662 case "gt": return ">";
1663 case "amp": return "&";
1664 case "quot": return "\"";
1665 case "apos": return "'";
1666 default: return null;
1671 private string ResolveAttributeValue (string unresolved)
1673 if(unresolved == null) return null;
1674 StringBuilder resolved = new StringBuilder();
1677 int next = unresolved.IndexOf ('&');
1683 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
1684 int endPos = unresolved.IndexOf (';', next+1);
1686 unresolved.Substring (next + 1, endPos - next - 1);
1687 if(entityName [0] == '#') {
1690 if(entityName [1] == 'x') {
1692 c = (char) int.Parse ("0" + entityName.Substring (2),
1693 System.Globalization.NumberStyles.HexNumber);
1696 c = (char) int.Parse (entityName.Substring (1));
1698 resolved.Append (c);
1700 switch(entityName) {
1701 case "lt": resolved.Append ("<"); break;
1702 case "gt": resolved.Append (">"); break;
1703 case "amp": resolved.Append ("&"); break;
1704 case "quot": resolved.Append ("\""); break;
1705 case "apos": resolved.Append ("'"); break;
1706 // With respect to "Value", MS document is helpless
1707 // and the implemention returns inconsistent value
1708 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
1709 default: resolved.Append ("&" + entityName + ";"); break;
1713 if(pos > unresolved.Length)
1715 next = unresolved.IndexOf('&', pos);
1717 resolved.Append (unresolved.Substring(pos));
1719 return resolved.ToString();