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 // I haven't checked whether DTD parser runs correct.
16 // More strict well-formedness checking should be done.
18 // NameTables aren't being used completely yet.
20 // Some thought needs to be given to performance. There's too many
21 // strings being allocated.
23 // Some of the MoveTo methods haven't been implemented yet.
25 // xml:space, xml:lang aren't being tracked.
29 using System.Collections;
33 using Mono.Xml.Native;
37 public class XmlTextReader : XmlReader, IXmlLineInfo
39 WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
42 protected XmlTextReader ()
46 public XmlTextReader (Stream input)
47 : this (new XmlStreamReader (input))
51 public XmlTextReader (string url)
52 : this(url, new NameTable ())
56 public XmlTextReader (TextReader input)
57 : this (input, new NameTable ())
61 protected XmlTextReader (XmlNameTable nt)
62 : this (String.Empty, null, XmlNodeType.None, null)
66 public XmlTextReader (Stream input, XmlNameTable nt)
67 : this(new XmlStreamReader (input), nt)
71 public XmlTextReader (string url, Stream input)
72 : this (url, new XmlStreamReader (input))
76 public XmlTextReader (string url, TextReader input)
77 : this (url, input, new NameTable ())
81 [MonoTODO("Non-filename-url must be supported. Waiting for WebClient")]
82 public XmlTextReader (string url, XmlNameTable nt)
83 : this (url, new XmlStreamReader (url), nt)
87 public XmlTextReader (TextReader input, XmlNameTable nt)
88 : this (String.Empty, input, nt)
92 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
93 : this (context.BaseURI, new XmlStreamReader (xmlFragment), fragType, context)
97 public XmlTextReader (string url, Stream input, XmlNameTable nt)
98 : this (url, new XmlStreamReader (input), nt)
102 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
103 : this (url, input, XmlNodeType.Document, null)
107 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
108 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
109 : this (context != null ? context.BaseURI : String.Empty,
110 new StringReader (xmlFragment),
116 // TODO still remains as described at head of this file,
117 // but it might not be TODO of the constructors...
118 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
120 this.Initialize (url, context, fragment, fragType);
127 public override int AttributeCount
129 get { return attributes.Count; }
132 public override string BaseURI
134 get { return parserContext.BaseURI; }
137 public override int Depth
144 public Encoding Encoding
146 get { return parserContext.Encoding; }
149 public override bool EOF
154 readState == ReadState.EndOfFile ||
155 readState == ReadState.Closed;
159 public override bool HasValue
161 get { return value != String.Empty; }
164 public override bool IsDefault
168 // XmlTextReader does not expand default attributes.
173 public override bool IsEmptyElement
175 get { return isEmptyElement; }
178 public override string this [int i]
180 get { return GetAttribute (i); }
183 public override string this [string name]
185 get { return GetAttribute (name); }
188 public override string this [string localName, string namespaceName]
190 get { return GetAttribute (localName, namespaceName); }
193 public int LineNumber
195 get { return currentInput.LineNumber; }
198 public int LinePosition
200 get { return currentInput.LinePosition; }
203 public override string LocalName
205 get { return localName; }
208 public override string Name
213 public bool Namespaces
215 get { return namespaces; }
217 if (readState != ReadState.Initial)
218 throw new InvalidOperationException ("Namespaces have to be set before reading.");
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 UnescapeAttributeValue(value);
276 public WhitespaceHandling WhitespaceHandling
278 get { return whitespaceHandling; }
279 set { whitespaceHandling = value; }
283 public override string XmlLang
285 get { throw new NotImplementedException (); }
288 public XmlResolver XmlResolver
290 set { resolver = value; }
294 public override XmlSpace XmlSpace
296 get { throw new NotImplementedException (); }
304 public override void Close ()
306 readState = ReadState.Closed;
309 public override string GetAttribute (int i)
311 if (i > attributes.Count)
312 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
314 return UnescapeAttributeValue (attributes [orderedAttributes [i]] as string);
317 public override string GetAttribute (string name)
319 return attributes.ContainsKey (name) ?
320 UnescapeAttributeValue (attributes [name] as string) : String.Empty;
323 public override string GetAttribute (string localName, string namespaceURI)
325 foreach (DictionaryEntry entry in attributes)
327 string thisName = entry.Key as string;
329 int indexOfColon = thisName.IndexOf (':');
331 if (indexOfColon != -1) {
332 string thisLocalName = thisName.Substring (indexOfColon + 1);
334 if (localName == thisLocalName) {
335 string thisPrefix = thisName.Substring (0, indexOfColon);
336 string thisNamespaceURI = LookupNamespace (thisPrefix);
338 if (namespaceURI == thisNamespaceURI)
339 return attributes.ContainsKey (thisName) ?
340 UnescapeAttributeValue (attributes [thisName] as string) : String.Empty;
342 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
343 return attributes.ContainsKey (thisName) ?
344 UnescapeAttributeValue (attributes [thisName] as string) : String.Empty;
351 public TextReader GetRemainder ()
353 throw new NotImplementedException ();
356 bool IXmlLineInfo.HasLineInfo ()
361 public override string LookupNamespace (string prefix)
363 return parserContext.NamespaceManager.LookupNamespace (prefix);
366 public override void MoveToAttribute (int i)
370 if (attributes == null || orderedAttributes.Count < i || i < 0)
371 throw new ArgumentOutOfRangeException ("attribute index out of range.");
373 string name = orderedAttributes [i] as string;
374 string value = attributes [name] as string;
376 XmlNodeType.Attribute, // nodeType
378 false, // isEmptyElement
380 false // clearAttributes
382 attributeValuePos = 0;
385 public override bool MoveToAttribute (string name)
390 if (attributes == null)
393 if (orderedAttributesEnumerator == null) {
395 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
398 while (orderedAttributesEnumerator.MoveNext ()) {
399 if(name == orderedAttributesEnumerator.Current as string) {
406 string value = attributes [name] as string;
408 XmlNodeType.Attribute, // nodeType
410 false, // isEmptyElement
412 false // clearAttributes
414 attributeValuePos = 0;
421 public override bool MoveToAttribute (string localName, string namespaceName)
423 throw new NotImplementedException ();
426 public override bool MoveToElement ()
428 if (orderedAttributesEnumerator != null) {
429 orderedAttributesEnumerator = null;
430 RestoreProperties ();
437 public override bool MoveToFirstAttribute ()
440 return MoveToNextAttribute ();
443 public override bool MoveToNextAttribute ()
445 if (attributes == null)
448 if (orderedAttributesEnumerator == null) {
450 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
453 if (orderedAttributesEnumerator.MoveNext ()) {
454 string name = orderedAttributesEnumerator.Current as string;
455 string value = attributes [name] as string;
457 XmlNodeType.Attribute, // nodeType
459 false, // isEmptyElement
461 false // clearAttributes
463 attributeValuePos = 0;
470 public override bool Read ()
474 readState = ReadState.Interactive;
476 more = ReadContent ();
481 public override bool ReadAttributeValue ()
483 // 'attributeString' holds real string value (without their
484 // quotation characters).
486 // 'attributeValuePos' holds current position
487 // of 'attributeString' while iterating ReadAttribute().
489 // -1 if ReadAttributeValue() has already finished.
490 // 0 if ReadAttributeValue() ready to start reading.
491 // >0 if ReadAttributeValue() already got 1 or more values
493 // local 'refPosition' holds the position on the
494 // attributeString which may be used next time.
496 if (attributeValuePos < 0) {
497 SetProperties (XmlNodeType.None,
505 // If not started, then initialize attributeString when parsing is at start.
506 if (attributeValuePos == 0)
508 value.Substring (1, value.Length - 2);
510 returnEntityReference = false;
511 value = String.Empty;
516 refPosition = attributeString.IndexOf ('&', attributeValuePos);
517 if (refPosition < 0) {
518 // Reached to the end of value string.
519 value += attributeString.Substring (attributeValuePos);
520 attributeValuePos = -1;
522 } else if (refPosition == attributeValuePos) {
523 string parsed = ReadAttributeValueReference ();
527 // Found that an entity reference starts from this point.
528 // reset position to after '&'.
529 attributeValuePos = refPosition;
530 if (value.Length <= 0) {
531 int endNamePos = attributeString.IndexOf (";", attributeValuePos);
532 value = attributeString.Substring (attributeValuePos+1, endNamePos - attributeValuePos - 1);
533 attributeValuePos += value.Length + 2;
534 returnEntityReference = true;
539 value += attributeString.Substring (attributeValuePos,
540 refPosition - attributeValuePos);
541 attributeValuePos = refPosition;
544 } while (++loop > 0);
546 if (returnEntityReference)
547 SetProperties (XmlNodeType.EntityReference,
553 SetProperties (XmlNodeType.Text,
563 public int ReadBase64 (byte [] buffer, int offset, int length)
565 throw new NotImplementedException ();
569 public int ReadBinHex (byte [] buffer, int offset, int length)
571 throw new NotImplementedException ();
575 public int ReadChars (char [] buffer, int offset, int length)
577 throw new NotImplementedException ();
580 public override string ReadInnerXml ()
582 if (readState != ReadState.Interactive)
586 case XmlNodeType.Attribute:
587 return value.Substring (1, value.Length - 2);
588 case XmlNodeType.Element:
592 int startDepth = depth;
594 if (innerXmlBuilder == null)
595 innerXmlBuilder = new StringBuilder ();
596 innerXmlBuilder.Length = 0;
599 if (NodeType != XmlNodeType.EndElement || depth + 1 > startDepth)
600 innerXmlBuilder.Append (currentTag);
601 } while (depth >= startDepth);
603 string xml = innerXmlBuilder.ToString ();
604 innerXmlBuilder.Length = 0;
606 case XmlNodeType.None:
607 // MS document is incorrect. Seems not to progress.
615 public override string ReadOuterXml ()
617 if (readState != ReadState.Interactive)
621 case XmlNodeType.Attribute:
622 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
623 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
624 case XmlNodeType.Element:
625 bool isEmpty = IsEmptyElement;
626 string startTag = currentTag.ToString ();
629 if (NodeType == XmlNodeType.Element && !isEmpty)
630 return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
632 return currentTag.ToString ();
633 case XmlNodeType.None:
634 // MS document is incorrect. Seems not to progress.
642 public override string ReadString ()
644 if (readStringBuffer == null)
645 readStringBuffer = new StringBuilder ();
646 readStringBuffer.Length = 0;
651 case XmlNodeType.Element:
657 case XmlNodeType.Text:
658 case XmlNodeType.CDATA:
659 case XmlNodeType.Whitespace:
660 case XmlNodeType.SignificantWhitespace:
661 readStringBuffer.Append (Value);
667 case XmlNodeType.Text:
668 case XmlNodeType.CDATA:
669 case XmlNodeType.Whitespace:
670 case XmlNodeType.SignificantWhitespace:
673 case XmlNodeType.Text:
674 case XmlNodeType.CDATA:
675 case XmlNodeType.Whitespace:
676 case XmlNodeType.SignificantWhitespace:
677 readStringBuffer.Append (Value);
685 string ret = readStringBuffer.ToString ();
686 readStringBuffer.Length = 0;
691 public void ResetState ()
693 throw new NotImplementedException ();
696 public override void ResolveEntity ()
698 // XmlTextReaders don't resolve entities.
699 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
705 // Parsed DTD Objects
706 internal DTDObjectModel currentSubset;
708 internal void Initialize (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
710 parserContext = context;
711 if (context == null) {
712 XmlNameTable nt = new NameTable ();
713 parserContext = new XmlParserContext (nt,
714 new XmlNamespaceManager (nt),
718 if (url != null && url != String.Empty)
719 parserContext.BaseURI = url;
723 case XmlNodeType.Attribute:
726 case XmlNodeType.Element:
727 allowMultipleRoot = true;
729 case XmlNodeType.Document:
732 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
735 this.currentInput = new XmlParserInput (fragment, url);
736 StreamReader sr = fragment as StreamReader;
742 private XmlParserContext parserContext;
744 private XmlParserInput currentInput;
745 private Stack parserInputStack = new Stack ();
746 private ReadState readState;
749 private int elementDepth;
750 private bool depthDown;
752 private bool popScope;
753 private Stack elementStack;
754 private Stack baseURIStack;
755 private bool haveEnteredDocument;
756 private bool allowMultipleRoot = false;
758 private XmlNodeType nodeType;
760 private string prefix;
761 private string localName;
762 private string namespaceURI;
763 private bool isEmptyElement;
764 private string value;
766 private XmlNodeType saveNodeType;
767 private string saveName;
768 private string savePrefix;
769 private string saveLocalName;
770 private string saveNamespaceURI;
771 private bool saveIsEmptyElement;
773 private Hashtable attributes;
774 private ArrayList orderedAttributes;
775 private IEnumerator orderedAttributesEnumerator;
777 private bool returnEntityReference;
778 private string entityReferenceName;
780 private char [] nameBuffer;
781 private int nameLength;
782 private int nameCapacity;
783 private const int initialNameCapacity = 256;
785 private char [] valueBuffer;
786 private int valueLength;
787 private int valueCapacity;
788 private const int initialValueCapacity = 8192;
790 // A buffer for ReadContent for ReadOuterXml
791 private StringBuilder currentTag {
793 return currentInput.CurrentMarkup;
797 private string attributeString = String.Empty;
798 private int attributeValuePos;
799 // This should be only referenced(used) by ReadInnerXml(). Kind of flyweight pattern.
800 private StringBuilder innerXmlBuilder;
801 private StringBuilder readStringBuffer;
803 // Parameter entity placeholder
804 private Hashtable parameterEntities = new Hashtable ();
807 private XmlResolver resolver = new XmlUrlResolver ();
809 private bool namespaces = true;
811 private XmlException ReaderError (string message)
813 return new XmlException (message, LineNumber, LinePosition);
818 readState = ReadState.Initial;
824 elementStack = new Stack();
825 baseURIStack = new Stack();
826 haveEnteredDocument = false;
828 nodeType = XmlNodeType.None;
830 prefix = String.Empty;
831 localName = string.Empty;
832 isEmptyElement = false;
833 value = String.Empty;
835 attributes = new Hashtable ();
836 orderedAttributes = new ArrayList ();
837 orderedAttributesEnumerator = null;
839 returnEntityReference = false;
840 entityReferenceName = String.Empty;
842 nameBuffer = new char [initialNameCapacity];
844 nameCapacity = initialNameCapacity;
846 valueBuffer = new char [initialValueCapacity];
848 valueCapacity = initialValueCapacity;
851 // Use this method rather than setting the properties
852 // directly so that all the necessary properties can
853 // be changed in harmony with each other. Maybe the
854 // fields should be in a seperate class to help enforce
856 private void SetProperties (
857 XmlNodeType nodeType,
861 bool clearAttributes)
863 this.nodeType = nodeType;
865 this.isEmptyElement = isEmptyElement;
867 this.elementDepth = depth;
873 int indexOfColon = name.IndexOf (':');
875 if (indexOfColon == -1) {
876 prefix = String.Empty;
879 prefix = name.Substring (0, indexOfColon);
880 localName = name.Substring (indexOfColon + 1);
883 prefix = String.Empty;
887 namespaceURI = LookupNamespace (prefix);
890 private void SaveProperties ()
892 saveNodeType = nodeType;
895 saveLocalName = localName;
896 saveNamespaceURI = namespaceURI;
897 saveIsEmptyElement = isEmptyElement;
898 // An element's value is always String.Empty.
901 private void RestoreProperties ()
903 nodeType = saveNodeType;
906 localName = saveLocalName;
907 namespaceURI = saveNamespaceURI;
908 isEmptyElement = saveIsEmptyElement;
909 value = String.Empty;
912 private void AddAttribute (string name, string value)
914 attributes.Add (name, value);
915 orderedAttributes.Add (name);
918 private void ClearAttributes ()
920 if (attributes.Count > 0) {
922 orderedAttributes.Clear ();
925 orderedAttributesEnumerator = null;
928 private int PeekChar ()
930 return currentInput.PeekChar ();
933 private int ReadChar ()
935 return currentInput.ReadChar ();
938 // This should really keep track of some state so
939 // that it's not possible to have more than one document
940 // element or text outside of the document element.
941 private bool ReadContent ()
943 currentTag.Length = 0;
945 parserContext.NamespaceManager.PopScope ();
949 if (returnEntityReference) {
950 SetEntityReferenceProperties ();
952 switch (PeekChar ()) {
957 case '\r': goto case ' ';
958 case '\n': goto case ' ';
959 case '\t': goto case ' ';
961 if (whitespaceHandling == WhitespaceHandling.All ||
962 whitespaceHandling == WhitespaceHandling.Significant)
963 return ReadWhitespace ();
966 return ReadContent ();
969 throw new XmlException ("unexpected end of file. Current depth is " + depth);
970 readState = ReadState.EndOfFile;
972 XmlNodeType.None, // nodeType
973 String.Empty, // name
974 false, // isEmptyElement
975 String.Empty, // value
976 true // clearAttributes
984 return this.ReadState != ReadState.EndOfFile;
987 private void SetEntityReferenceProperties ()
990 XmlNodeType.EntityReference, // nodeType
991 entityReferenceName, // name
992 false, // isEmptyElement
993 String.Empty, // value
994 true // clearAttributes
997 returnEntityReference = false;
998 entityReferenceName = String.Empty;
1001 // The leading '<' has already been consumed.
1002 private void ReadTag ()
1004 switch (PeekChar ())
1012 ReadProcessingInstruction ();
1024 // The leading '<' has already been consumed.
1025 private void ReadStartTag ()
1027 parserContext.NamespaceManager.PushScope ();
1029 string name = ReadName ();
1030 if (haveEnteredDocument && elementStack.Count == 0 && !allowMultipleRoot)
1031 throw ReaderError("document has terminated, cannot open new element");
1033 haveEnteredDocument = true;
1036 bool isEmptyElement = false;
1040 if (XmlConstructs.IsNameStart (PeekChar ()))
1043 if (PeekChar () == '/') {
1045 isEmptyElement = true;
1050 elementStack.Push (name);
1051 baseURIStack.Push (attributes ["xml:base"] != null ?
1052 attributes ["xml:base"] : BaseURI);
1058 XmlNodeType.Element, // nodeType
1060 isEmptyElement, // isEmptyElement
1061 String.Empty, // value
1062 false // clearAttributes
1072 // The reader is positioned on the first character
1073 // of the element's name.
1074 private void ReadEndTag ()
1076 string name = ReadName ();
1077 if (elementStack.Count == 0)
1078 throw ReaderError("closing element without matching opening element");
1079 if ((string)elementStack.Pop() != name)
1080 throw ReaderError("unmatched closing element");
1081 baseURIStack.Pop ();
1089 XmlNodeType.EndElement, // nodeType
1091 false, // isEmptyElement
1092 String.Empty, // value
1093 true // clearAttributes
1099 private void AppendNameChar (int ch)
1101 CheckNameCapacity ();
1102 nameBuffer [nameLength++] = (char)ch;
1105 private void CheckNameCapacity ()
1107 if (nameLength == nameCapacity) {
1108 nameCapacity = nameCapacity * 2;
1109 char [] oldNameBuffer = nameBuffer;
1110 nameBuffer = new char [nameCapacity];
1111 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1115 private string CreateNameString ()
1117 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1120 private void AppendValueChar (int ch)
1122 CheckValueCapacity ();
1123 valueBuffer [valueLength++] = (char)ch;
1126 private void CheckValueCapacity ()
1128 if (valueLength == valueCapacity) {
1129 valueCapacity = valueCapacity * 2;
1130 char [] oldValueBuffer = valueBuffer;
1131 valueBuffer = new char [valueCapacity];
1132 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1136 private string CreateValueString ()
1138 return new String (valueBuffer, 0, valueLength);
1141 // The reader is positioned on the first character
1143 private void ReadText (bool cleanValue)
1148 int ch = PeekChar ();
1150 while (ch != '<' && ch != -1) {
1153 if (ReadReference (false))
1156 AppendValueChar (ReadChar ());
1161 if (returnEntityReference && valueLength == 0) {
1162 SetEntityReferenceProperties ();
1165 XmlNodeType.Text, // nodeType
1166 String.Empty, // name
1167 false, // isEmptyElement
1168 CreateValueString (), // value
1169 true // clearAttributes
1174 // The leading '&' has already been consumed.
1175 // Returns true if the entity reference isn't a simple
1176 // character reference or one of the predefined entities.
1177 // This allows the ReadText method to break so that the
1178 // next call to Read will return the EntityReference node.
1179 private bool ReadReference (bool ignoreEntityReferences)
1181 if (PeekChar () == '#') {
1183 ReadCharacterReference ();
1185 ReadEntityReference (ignoreEntityReferences);
1187 return returnEntityReference;
1190 private void ReadCharacterReference ()
1194 if (PeekChar () == 'x') {
1197 while (PeekChar () != ';' && PeekChar () != -1) {
1198 int ch = ReadChar ();
1200 if (ch >= '0' && ch <= '9')
1201 value = (value << 4) + ch - '0';
1202 else if (ch >= 'A' && ch <= 'F')
1203 value = (value << 4) + ch - 'A' + 10;
1204 else if (ch >= 'a' && ch <= 'f')
1205 value = (value << 4) + ch - 'a' + 10;
1209 "invalid hexadecimal digit: {0} (#x{1:X})",
1214 while (PeekChar () != ';' && PeekChar () != -1) {
1215 int ch = ReadChar ();
1217 if (ch >= '0' && ch <= '9')
1218 value = value * 10 + ch - '0';
1222 "invalid decimal digit: {0} (#x{1:X})",
1230 AppendValueChar (value);
1233 private void ReadEntityReference (bool ignoreEntityReferences)
1237 int ch = PeekChar ();
1239 while (ch != ';' && ch != -1) {
1240 AppendNameChar (ReadChar ());
1246 string name = CreateNameString ();
1251 AppendValueChar ('<');
1254 AppendValueChar ('>');
1257 AppendValueChar ('&');
1260 AppendValueChar ('\'');
1263 AppendValueChar ('"');
1266 if (ignoreEntityReferences) {
1267 AppendValueChar ('&');
1269 foreach (char ch2 in name) {
1270 AppendValueChar (ch2);
1273 AppendValueChar (';');
1275 returnEntityReference = true;
1276 entityReferenceName = name;
1282 // The reader is positioned on the first character of
1283 // the attribute name.
1284 private void ReadAttributes ()
1287 string name = ReadName ();
1291 string value = ReadAttribute ();
1294 if (name == "xmlns")
1295 parserContext.NamespaceManager.AddNamespace (String.Empty, UnescapeAttributeValue (value));
1296 else if (name.StartsWith ("xmlns:"))
1297 parserContext.NamespaceManager.AddNamespace (name.Substring (6), UnescapeAttributeValue (value));
1299 AddAttribute (name, value);
1300 } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1303 // The reader is positioned on the quote character.
1304 // *Keeps quote char* to value to get_QuoteChar() correctly.
1305 private string ReadAttribute ()
1309 int quoteChar = ReadChar ();
1311 if (quoteChar != '\'' && quoteChar != '\"')
1312 throw ReaderError ("an attribute value was not quoted");
1314 AppendValueChar (quoteChar);
1316 while (PeekChar () != quoteChar) {
1317 int ch = ReadChar ();
1322 throw ReaderError ("attribute values cannot contain '<'");
1324 throw ReaderError ("unexpected end of file in an attribute value");
1326 AppendValueChar (ch);
1331 ReadChar (); // quoteChar
1332 AppendValueChar (quoteChar);
1334 return CreateValueString ();
1337 // The reader is positioned on the first character
1340 // Now it also reads XmlDeclaration, this method name became improper...
1341 private void ReadProcessingInstruction ()
1343 string target = ReadName ();
1348 while (PeekChar () != -1) {
1349 int ch = ReadChar ();
1351 if (ch == '?' && PeekChar () == '>') {
1356 AppendValueChar ((char)ch);
1361 XmlNodeType.XmlDeclaration :
1362 XmlNodeType.ProcessingInstruction, // nodeType
1364 false, // isEmptyElement
1365 CreateValueString (), // value
1366 true // clearAttributes
1370 // The reader is positioned on the first character after
1371 // the leading '<!'.
1372 private void ReadDeclaration ()
1374 int ch = PeekChar ();
1394 // The reader is positioned on the first character after
1395 // the leading '<!--'.
1396 private void ReadComment ()
1400 while (PeekChar () != -1) {
1401 int ch = ReadChar ();
1403 if (ch == '-' && PeekChar () == '-') {
1406 if (PeekChar () != '>')
1407 throw ReaderError ("comments cannot contain '--'");
1413 AppendValueChar ((char)ch);
1417 XmlNodeType.Comment, // nodeType
1418 String.Empty, // name
1419 false, // isEmptyElement
1420 CreateValueString (), // value
1421 true // clearAttributes
1425 // The reader is positioned on the first character after
1426 // the leading '<![CDATA['.
1427 private void ReadCDATA ()
1433 while (PeekChar () != -1) {
1438 if (ch == ']' && PeekChar () == ']') {
1439 ch = ReadChar (); // ']'
1441 if (PeekChar () == '>') {
1446 // AppendValueChar (']');
1447 // AppendValueChar (']');
1448 // ch = ReadChar ();
1452 AppendValueChar ((char)ch);
1456 XmlNodeType.CDATA, // nodeType
1457 String.Empty, // name
1458 false, // isEmptyElement
1459 CreateValueString (), // value
1460 true // clearAttributes
1464 // The reader is positioned on the first character after
1465 // the leading '<!DOCTYPE'.
1466 private void ReadDoctypeDecl ()
1468 string doctypeName = null;
1469 string publicId = String.Empty;
1470 string systemId = String.Empty;
1471 int intSubsetStartLine = 0;
1472 int intSubsetStartColumn = 0;
1475 doctypeName = ReadName ();
1480 systemId = ReadSystemLiteral (true);
1483 publicId = ReadPubidLiteral ();
1485 systemId = ReadSystemLiteral (false);
1491 if(PeekChar () == '[')
1493 // read markupdecl etc. or end of decl
1495 intSubsetStartLine = this.LineNumber;
1496 intSubsetStartColumn = this.LinePosition;
1497 int startPos = currentTag.Length;
1499 ReadInternalSubset ();
1500 // } while (nodeType != XmlNodeType.None);
1501 int endPos = currentTag.Length - 1;
1502 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1504 // end of DOCTYPE decl.
1509 currentSubset = new DTDObjectModel (); // merges both internal and external subsets in the meantime,
1510 int originalParserDepth = parserInputStack.Count;
1511 if (intSubsetStartLine > 0) {
1512 XmlParserInput original = currentInput;
1513 currentInput = new XmlParserInput (new StringReader (parserContext.InternalSubset), BaseURI, intSubsetStartLine, intSubsetStartColumn);
1515 CompileDTDSubset ();
1516 if (PeekChar () == -1 && parserInputStack.Count > 0)
1518 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth);
1519 if (dtdIncludeSect != 0)
1520 this.ReaderError ("INCLUDE section is not ended correctly.");
1521 currentInput = original;
1523 if (systemId != String.Empty) {
1524 pushParserInput (systemId);
1526 this.CompileDTDSubset ();
1527 if (PeekChar () == -1 && parserInputStack.Count > 1)
1529 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth + 1);
1533 // set properties for <!DOCTYPE> node
1535 XmlNodeType.DocumentType, // nodeType
1536 doctypeName, // name
1537 false, // isEmptyElement
1538 parserContext.InternalSubset, // value
1539 true // clearAttributes
1543 private void pushParserInput (string url)
1545 string absPath = null;
1548 Uri baseUrl = new Uri (BaseURI);
1549 absPath = resolver.ResolveUri (baseUrl, url).ToString ();
1550 } catch (UriFormatException) {
1551 if (Path.IsPathRooted (url))
1553 else if (BaseURI != String.Empty)
1554 absPath = new FileInfo (BaseURI).DirectoryName + Path.DirectorySeparatorChar + url;
1559 if (Path.IsPathRooted (url))
1561 else if (BaseURI != String.Empty)
1562 absPath = new FileInfo (BaseURI).DirectoryName + Path.DirectorySeparatorChar + url;
1566 foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1567 if (i.BaseURI == url)
1568 this.ReaderError ("Nested inclusion is not allowed: " + url);
1570 parserInputStack.Push (currentInput);
1571 currentInput = new XmlParserInput (new XmlStreamReader (absPath, false), absPath);
1572 baseURIStack.Push (BaseURI);
1573 parserContext.BaseURI = absPath;
1576 private void popParserInput ()
1578 currentInput = parserInputStack.Pop () as XmlParserInput;
1579 parserContext.BaseURI = this.baseURIStack.Pop () as string;
1582 private enum DtdInputState
\r
1591 InsideSingleQuoted,
\r
1592 InsideDoubleQuoted,
\r
1595 private class DtdInputStateStack
\r
1597 Stack intern = new Stack ();
\r
1598 public DtdInputStateStack ()
\r
1600 Push (DtdInputState.Free);
\r
1603 public DtdInputState Peek ()
\r
1605 return (DtdInputState) intern.Peek ();
\r
1608 public DtdInputState Pop ()
\r
1610 return (DtdInputState) intern.Pop ();
\r
1613 public void Push (DtdInputState val)
\r
1615 intern.Push (val);
\r
1620 DtdInputStateStack stateStack = new DtdInputStateStack ();
\r
1621 DtdInputState State {
\r
1622 get { return stateStack.Peek (); }
\r
1625 // Simply read but not generate any result.
1626 private void ReadInternalSubset ()
\r
1628 bool continueParse = true;
\r
1630 while (continueParse) {
\r
1631 switch (ReadChar ()) {
\r
1634 case DtdInputState.Free:
\r
1635 continueParse = false;
\r
1637 case DtdInputState.InsideDoubleQuoted:
\r
1639 case DtdInputState.InsideSingleQuoted:
\r
1642 throw ReaderError ("unexpected end of file at DTD.");
\r
1646 throw ReaderError ("unexpected end of file at DTD.");
\r
1648 if (State == DtdInputState.InsideDoubleQuoted ||
\r
1649 State == DtdInputState.InsideSingleQuoted)
\r
1650 continue; // well-formed
\r
1651 switch (ReadChar ()) {
\r
1653 stateStack.Push (DtdInputState.PI);
\r
1656 switch (ReadChar ()) {
\r
1658 switch (ReadChar ()) {
\r
1661 stateStack.Push (DtdInputState.ElementDecl);
\r
1665 stateStack.Push (DtdInputState.EntityDecl);
\r
1668 throw ReaderError ("unexpected token '<!E'.");
\r
1672 Expect ("TTLIST");
\r
1673 stateStack.Push (DtdInputState.AttlistDecl);
\r
1676 Expect ("OTATION");
\r
1677 stateStack.Push (DtdInputState.NotationDecl);
\r
1681 stateStack.Push (DtdInputState.Comment);
\r
1686 throw ReaderError ("unexpected '>'.");
\r
1690 if (State == DtdInputState.InsideSingleQuoted)
\r
1691 stateStack.Pop ();
\r
1692 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
\r
1693 stateStack.Push (DtdInputState.InsideSingleQuoted);
\r
1696 if (State == DtdInputState.InsideDoubleQuoted)
\r
1697 stateStack.Pop ();
\r
1698 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
\r
1699 stateStack.Push (DtdInputState.InsideDoubleQuoted);
\r
1703 case DtdInputState.ElementDecl:
\r
1704 goto case DtdInputState.NotationDecl;
\r
1705 case DtdInputState.AttlistDecl:
\r
1706 goto case DtdInputState.NotationDecl;
\r
1707 case DtdInputState.EntityDecl:
\r
1708 goto case DtdInputState.NotationDecl;
\r
1709 case DtdInputState.NotationDecl:
\r
1710 stateStack.Pop ();
\r
1712 case DtdInputState.InsideDoubleQuoted:
\r
1714 case DtdInputState.InsideSingleQuoted:
\r
1715 continue; // well-formed
\r
1716 case DtdInputState.Comment:
\r
1719 throw ReaderError ("unexpected token '>'");
\r
1723 if (State == DtdInputState.PI) {
\r
1724 if (ReadChar () == '>')
\r
1725 stateStack.Pop ();
\r
1729 if (State == DtdInputState.Comment) {
\r
1730 if (PeekChar () == '-') {
\r
1733 stateStack.Pop ();
\r
1738 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
\r
1739 throw ReaderError ("Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
\r
1745 // Read any one of following:
1746 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1747 // PI, Comment, Parameter Entity, or doctype termination char(']')
1749 // returns a node of some nodeType or null, setting nodeType.
1750 // (if None then ']' was found.)
1751 private void CompileDTDSubset()
1757 nodeType = XmlNodeType.None;
1767 // Only read, no store.
1768 ReadProcessingInstruction ();
1771 CompileDeclaration ();
1774 throw ReaderError ("Syntax Error after '<' character.");
1784 throw ReaderError (String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
1788 private void CompileDeclaration ()
1790 nodeType = XmlNodeType.DocumentType; // Hack!!
1795 // Only read, no store.
1805 if (PeekChar () == '%') {
1807 if (!XmlConstructs.IsSpace (PeekChar ())) {
1810 // throw ReaderError ("expected whitespace between '%' and name.");
1814 if (XmlConstructs.IsName (PeekChar ()))
1815 ReadParameterEntityDecl ();
1817 throw ReaderError ("expected name character");
1821 DTDEntityDeclaration ent = ReadEntityDecl ();
1822 if (currentSubset.EntityDecls [ent.Name] == null)
1823 currentSubset.EntityDecls.Add (ent.Name, ent);
1827 DTDElementDeclaration el = ReadElementDecl ();
1828 currentSubset.ElementDecls.Add (el.Name, el);
1831 throw ReaderError ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1836 DTDAttListDeclaration atl = ReadAttListDecl ();
1837 if (currentSubset.AttListDecls.ContainsKey (atl.Name))
1838 currentSubset.AttListDecls.Add (atl.Name, atl);
1842 DTDNotationDeclaration not = ReadNotationDecl ();
1843 currentSubset.NotationDecls.Add (not.Name, not);
1846 // conditional sections
1851 switch (ReadChar ()) {
1865 throw ReaderError ("Syntax Error after '<!' characters.");
1869 private void ReadIgnoreSect ()
1874 int dtdIgnoreSect = 1;
1875 while (dtdIgnoreSect > 0) {
1876 switch (skip ? PeekChar () : ReadChar ()) {
1878 throw ReaderError ("Unexpected IGNORE section end.");
1881 if (ReadChar () == '!' && ReadChar () == '[')
1885 if (ReadChar () == ']') {
1886 if (ReadChar () == '>')
1897 // The reader is positioned on the head of the name.
\r
1898 private DTDElementDeclaration ReadElementDecl ()
\r
1900 DTDElementDeclaration decl = new DTDElementDeclaration ();
\r
1901 SkipWhitespace ();
\r
1902 TryExpandPERef ();
\r
1903 decl.Name = ReadName ();
\r
1904 SkipWhitespace ();
\r
1905 TryExpandPERef ();
\r
1906 ReadContentSpec (decl);
\r
1907 SkipWhitespace ();
\r
1912 // read 'children'(BNF) of contentspec
\r
1913 private void ReadContentSpec (DTDElementDeclaration decl)
\r
1915 switch(PeekChar ())
\r
1918 decl.IsEmpty = true;
\r
1922 decl.IsAny = true;
\r
1926 DTDContentModel model = decl.ContentModel;
\r
1928 SkipWhitespace ();
\r
1929 TryExpandPERef ();
\r
1930 if(PeekChar () == '#') {
\r
1932 decl.IsMixedContent = true;
\r
1933 Expect ("#PCDATA");
\r
1934 SkipWhitespace ();
\r
1935 TryExpandPERef ();
\r
1936 SkipWhitespace ();
\r
1937 while(PeekChar () != ')') {
\r
1939 SkipWhitespace ();
\r
1940 TryExpandPERef ();
\r
1941 SkipWhitespace ();
\r
1942 model.ChildModels.Add (ReadName ());
\r
1943 SkipWhitespace ();
\r
1944 TryExpandPERef ();
\r
1947 if(PeekChar () == '*')
\r
1948 ReadChar (); // ZeroOrMore
\r
1950 // Non-Mixed Contents
\r
1951 model.ChildModels.Add (ReadCP ());
\r
1952 SkipWhitespace ();
\r
1954 do { // copied from ReadCP() ...;-)
\r
1955 TryExpandPERef ();
\r
1956 SkipWhitespace ();
\r
1957 if(PeekChar ()=='|') {
\r
1959 model.OrderType = DTDContentOrderType.Or;
\r
1961 SkipWhitespace ();
\r
1962 model.ChildModels.Add (ReadCP ());
\r
1963 SkipWhitespace ();
\r
1965 else if(PeekChar () == ',')
\r
1968 model.OrderType = DTDContentOrderType.Seq;
\r
1970 SkipWhitespace ();
\r
1971 model.ChildModels.Add (ReadCP ());
\r
1972 SkipWhitespace ();
\r
1980 switch(PeekChar ())
\r
1983 model.MinOccurs = 0;
\r
1987 model.MinOccurs = 0;
\r
1988 model.MaxOccurs = decimal.MaxValue;
\r
1992 model.MaxOccurs = decimal.MaxValue;
\r
1996 SkipWhitespace ();
\r
1998 SkipWhitespace ();
\r
2003 // Read 'cp' (BNF) of contentdecl (BNF)
\r
2004 private DTDContentModel ReadCP ()
\r
2006 DTDContentModel model = new DTDContentModel ();
\r
2007 TryExpandPERef ();
\r
2008 if(PeekChar () == '(') {
\r
2010 SkipWhitespace ();
\r
2011 model.ChildModels.Add (ReadCP ());
\r
2012 SkipWhitespace ();
\r
2014 TryExpandPERef ();
\r
2015 SkipWhitespace ();
\r
2016 if(PeekChar ()=='|') {
\r
2018 model.OrderType = DTDContentOrderType.Or;
\r
2020 SkipWhitespace ();
\r
2021 model.ChildModels.Add (ReadCP ());
\r
2022 SkipWhitespace ();
\r
2024 else if(PeekChar () == ',') {
\r
2026 model.OrderType = DTDContentOrderType.Seq;
\r
2028 SkipWhitespace ();
\r
2029 model.ChildModels.Add (ReadCP ());
\r
2030 SkipWhitespace ();
\r
2036 SkipWhitespace ();
\r
2040 TryExpandPERef ();
\r
2041 model.ElementName = ReadName ();
\r
2044 switch(PeekChar ()) {
\r
2046 model.MinOccurs = 0;
\r
2050 model.MinOccurs = 0;
\r
2051 model.MaxOccurs = decimal.MaxValue;
\r
2055 model.MaxOccurs = decimal.MaxValue;
\r
2062 // The reader is positioned on the first name char.
\r
2063 private void ReadParameterEntityDecl ()
\r
2065 DTDParameterEntityDeclaration decl =
\r
2066 new DTDParameterEntityDeclaration();
\r
2067 decl.BaseURI = BaseURI;
\r
2069 decl.Name = ReadName ();
\r
2070 SkipWhitespace ();
\r
2072 if (PeekChar () == 'S' || PeekChar () == 'P') {
\r
2073 // throw new NotImplementedException ("External parameter entity reference is not implemented yet.");
\r
2074 // read publicId/systemId
\r
2075 ReadExternalID ();
\r
2076 decl.PublicId = attributes ["PUBLIC"] as string;
\r
2077 decl.SystemId = attributes ["SYSTEM"] as string;
\r
2078 SkipWhitespace ();
\r
2081 TryExpandPERef ();
\r
2082 int quoteChar = ReadChar ();
\r
2083 int start = currentTag.Length;
\r
2085 SkipWhitespace ();
\r
2086 int c = PeekChar ();
\r
2087 if ((int) c == -1)
\r
2088 throw new XmlException ("unexpected end of stream in entity value definition.");
\r
2092 if (quoteChar == '"') goto SKIP;
\r
2096 if (quoteChar == '\'') goto SKIP;
\r
2107 decl.Value = currentTag.ToString (start, currentTag.Length - start - 1);
\r
2109 SkipWhitespace ();
\r
2111 if (parameterEntities [decl.Name] == null) {
\r
2112 parameterEntities.Add (decl.Name, decl);
\r
2116 // reader is positioned on '%'
\r
2117 private void ImportAsPERef ()
\r
2119 StringBuilder sb = null;
\r
2120 int peRefStart = currentTag.Length;
\r
2121 string appendStr = "";
\r
2123 string peName = ReadName ();
\r
2125 DTDParameterEntityDeclaration peDecl =
\r
2126 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
\r
2127 if (peDecl == null)
\r
2128 throw ReaderError ("Parameter entity " + peName + " not found.");
\r
2129 if (peDecl.SystemId != null) {
\r
2130 pushParserInput (peDecl.SystemId);
\r
2132 sb = new StringBuilder ();
\r
2135 while (PeekChar () != -1)
\r
2136 sb.Append (ReadChar ());
\r
2137 popParserInput ();
\r
2138 appendStr = sb.ToString ();
\r
2140 appendStr = peDecl.Value;
\r
2142 currentTag.Remove (peRefStart,
\r
2143 currentTag.Length - peRefStart);
\r
2144 currentTag.Append (Dereference (appendStr));
\r
2147 // The reader is positioned on the head of the name.
\r
2148 private DTDEntityDeclaration ReadEntityDecl ()
\r
2150 DTDEntityDeclaration decl = new DTDEntityDeclaration ();
\r
2151 decl.Name = ReadName ();
\r
2152 SkipWhitespace ();
\r
2153 TryExpandPERef ();
\r
2154 SkipWhitespace ();
\r
2156 if (PeekChar () == 'S' || PeekChar () == 'P') {
\r
2157 // external entity
\r
2158 ReadExternalID ();
\r
2159 decl.PublicId = attributes ["PUBLIC"] as string;
\r
2160 decl.SystemId = attributes ["SYSTEM"] as string;
\r
2161 SkipWhitespace ();
\r
2162 if (PeekChar () == 'N')
\r
2166 SkipWhitespace ();
\r
2167 decl.NotationName = ReadName (); // ndata_name
\r
2172 decl.EntityValue = ReadEntityValueDecl ();
\r
2174 SkipWhitespace ();
\r
2179 private string ReadEntityValueDecl ()
\r
2181 SkipWhitespace ();
\r
2182 // quotation char will be finally removed on unescaping
\r
2183 int quoteChar = ReadChar ();
\r
2184 int start = currentTag.Length;
\r
2185 if (quoteChar != '\'' && quoteChar != '"')
\r
2186 throw new XmlException ("quotation char was expected.");
\r
2188 while (PeekChar () != quoteChar) {
\r
2189 switch (PeekChar ()) {
\r
2191 this.ImportAsPERef ();
\r
2196 // ReadCharacterReference ();
\r
2197 ReadReference (true);
\r
2200 throw new XmlException ("unexpected end of stream.");
\r
2206 string value = Dereference (currentTag.ToString (start, currentTag.Length - start));
\r
2207 Expect (quoteChar);
\r
2211 private DTDAttListDeclaration ReadAttListDecl ()
\r
2213 SkipWhitespace ();
\r
2214 TryExpandPERef ();
\r
2215 string name = ReadName (); // target element name
\r
2216 DTDAttListDeclaration decl =
\r
2217 currentSubset.AttListDecls [name] as DTDAttListDeclaration;
\r
2219 decl = new DTDAttListDeclaration ();
\r
2222 SkipWhitespace ();
\r
2223 TryExpandPERef ();
\r
2224 SkipWhitespace ();
\r
2226 while (XmlConstructs.IsName ((char) PeekChar ())) {
\r
2227 DTDAttributeDefinition def = ReadAttributeDefinition ();
\r
2228 if (decl.AttributeDefinitions [def.Name] == null)
\r
2229 decl.AttributeDefinitions.Add (def.Name, def);
\r
2230 SkipWhitespace ();
\r
2231 TryExpandPERef ();
\r
2232 SkipWhitespace ();
\r
2234 SkipWhitespace ();
\r
2239 private DTDAttributeDefinition ReadAttributeDefinition ()
\r
2241 DTDAttributeDefinition def = new DTDAttributeDefinition ();
\r
2244 TryExpandPERef ();
\r
2245 def.Name = ReadName ();
\r
2246 SkipWhitespace ();
\r
2249 TryExpandPERef ();
\r
2250 switch(PeekChar ()) {
\r
2251 case 'C': // CDATA
\r
2253 def.AttributeType = DTDAttributeType.CData;
\r
2255 case 'I': // ID, IDREF, IDREFS
\r
2257 if(PeekChar () == 'R') {
\r
2259 if(PeekChar () == 'S') {
\r
2262 def.AttributeType = DTDAttributeType.IdRefs;
\r
2265 def.AttributeType = DTDAttributeType.IdRef;
\r
2268 def.AttributeType = DTDAttributeType.Id;
\r
2270 case 'E': // ENTITY, ENTITIES
\r
2272 switch(ReadChar ()) {
\r
2273 case 'Y': // ENTITY
\r
2274 def.AttributeType = DTDAttributeType.Entity;
\r
2276 case 'I': // ENTITIES
\r
2278 def.AttributeType = DTDAttributeType.Entities;
\r
2282 case 'N': // NMTOKEN, NMTOKENS, NOTATION
\r
2284 switch(PeekChar ()) {
\r
2286 Expect ("MTOKEN");
\r
2287 if(PeekChar ()=='S') { // NMTOKENS
\r
2289 def.AttributeType = DTDAttributeType.NmTokens;
\r
2292 def.AttributeType = DTDAttributeType.NmToken;
\r
2295 Expect ("OTATION");
\r
2296 def.AttributeType = DTDAttributeType.Notation;
\r
2297 SkipWhitespace ();
\r
2299 SkipWhitespace ();
\r
2300 def.EnumeratedNotations.Add (ReadName ()); // notation name
\r
2301 SkipWhitespace ();
\r
2302 while(PeekChar () == '|') {
\r
2304 SkipWhitespace ();
\r
2305 def.EnumeratedNotations.Add (ReadName ()); // notation name
\r
2306 SkipWhitespace ();
\r
2311 throw new XmlException ("attribute declaration syntax error.");
\r
2314 default: // Enumerated Values
\r
2315 TryExpandPERef ();
\r
2317 SkipWhitespace ();
\r
2318 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
\r
2319 SkipWhitespace ();
\r
2320 while(PeekChar () == '|') {
\r
2322 SkipWhitespace ();
\r
2323 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
\r
2324 SkipWhitespace ();
\r
2329 SkipWhitespace ();
\r
2331 TryExpandPERef ();
\r
2334 if(PeekChar () == '#')
\r
2337 switch(PeekChar ())
\r
2340 Expect ("REQUIRED");
\r
2341 def.OccurenceType = DTDAttributeOccurenceType.Required;
\r
2344 Expect ("IMPLIED");
\r
2345 def.OccurenceType = DTDAttributeOccurenceType.Optional;
\r
2349 def.OccurenceType = DTDAttributeOccurenceType.Fixed;
\r
2350 SkipWhitespace ();
\r
2351 def.UnresolvedDefaultValue = ReadAttribute ();
\r
2355 // one of the enumerated value
\r
2356 if (PeekChar () == -1) {
\r
2357 popParserInput ();
\r
2359 SkipWhitespace ();
\r
2360 def.UnresolvedDefaultValue = ReadAttribute ();
\r
2366 private DTDNotationDeclaration ReadNotationDecl()
\r
2368 DTDNotationDeclaration decl = new DTDNotationDeclaration ();
\r
2369 SkipWhitespace ();
\r
2370 decl.Name = ReadName (); // notation name
\r
2371 if (namespaces) { // copy from SetProperties ;-)
2372 int indexOfColon = decl.Name.IndexOf (':');
2374 if (indexOfColon == -1) {
2375 decl.Prefix = String.Empty;
2376 decl.LocalName = decl.Name;
2378 decl.Prefix = decl.Name.Substring (0, indexOfColon);
2379 decl.LocalName = decl.Name.Substring (indexOfColon + 1);
2382 decl.Prefix = String.Empty;
2383 decl.LocalName = decl.Name;
2386 SkipWhitespace ();
\r
2387 if(PeekChar () == 'P') {
\r
2388 decl.PublicId = ReadPubidLiteral ();
\r
2389 SkipWhitespace ();
\r
2390 if (PeekChar () == '\'' || PeekChar () == '"') {
\r
2391 decl.SystemId = ReadSystemLiteral (false);
\r
2392 SkipWhitespace ();
\r
2394 } else if(PeekChar () == 'S') {
\r
2395 decl.SystemId = ReadSystemLiteral (true);
\r
2396 SkipWhitespace ();
\r
2398 if(decl.PublicId == null && decl.SystemId == null)
\r
2399 throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
\r
2404 private void TryExpandPERef ()
\r
2406 if (PeekChar () == '%') {
\r
2408 if (!XmlConstructs.IsName (PeekChar ()))
\r
2414 // reader is positioned on the first letter of the name.
\r
2415 private void ExpandPERef ()
\r
2417 ExpandPERef (true);
\r
2420 private void ExpandPERef (bool attachSpace)
\r
2422 string peName = ReadName ();
\r
2424 ExpandNamedPERef (peName, attachSpace);
\r
2427 private void ExpandNamedPERef (string peName, bool attachSpace)
\r
2429 DTDParameterEntityDeclaration decl =
\r
2430 parameterEntities [peName] as DTDParameterEntityDeclaration;
\r
2432 throw new XmlException ("undeclared parameter entity: '" + peName + "'");
\r
2433 if (decl.SystemId != null) {
\r
2434 pushParserInput (decl.SystemId);
\r
2438 currentInput.InsertParameterEntityBuffer (attachSpace ? " " + Dereference (decl.Value) + " " : decl.Value);
\r
2439 SkipWhitespace (); // is it ok?
\r
2440 // while (PeekChar () == '%')
\r
2441 // TryExpandPERef (); // recursive
\r
2444 private void ReadExternalID() {
\r
2445 switch(PeekChar ()) {
\r
2447 attributes ["PUBLIC"] = null;
\r
2448 attributes ["SYSTEM"] = ReadSystemLiteral (true);
\r
2451 attributes ["PUBLIC"] = ReadPubidLiteral ();
\r
2452 SkipWhitespace ();
\r
2453 attributes ["SYSTEM"] = ReadSystemLiteral (false);
\r
2458 // The reader is positioned on the first 'S' of "SYSTEM".
2459 private string ReadSystemLiteral (bool expectSYSTEM)
2464 int quoteChar = ReadChar (); // apos or quot
2465 int startPos = currentTag.Length;
2467 while(c != quoteChar) {
2469 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
2471 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2474 private string ReadPubidLiteral()
2478 int quoteChar = ReadChar ();
2479 int startPos = currentTag.Length;
2481 while(c != quoteChar)
2484 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
2485 if(c != quoteChar && !XmlConstructs.IsPubid (c))
2486 throw ReaderError("character '" + (char)c + "' not allowed for PUBLIC ID");
2488 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2491 // The reader is positioned on the first character
\r
2493 internal string ReadName ()
\r
2495 return ReadNameOrNmToken(false);
\r
2498 // The reader is positioned on the first character
\r
2500 private string ReadNmToken ()
\r
2502 return ReadNameOrNmToken(true);
\r
2505 private string ReadNameOrNmToken(bool isNameToken)
\r
2507 int ch = PeekChar ();
\r
2509 if (!XmlConstructs.IsName ((char) ch))
\r
2510 throw ReaderError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
\r
2513 if (!XmlConstructs.IsNameStart ((char) PeekChar ()))
\r
2514 throw ReaderError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
\r
2519 AppendNameChar (ReadChar ());
2521 while (XmlConstructs.IsName (PeekChar ())) {
2522 AppendNameChar (ReadChar ());
2525 return CreateNameString ();
2528 // Read the next character and compare it against the
2529 // specified character.
2530 private void Expect (int expected)
2532 int ch = ReadChar ();
2534 if (ch != expected) {
2537 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2545 private void Expect (string expected)
2547 int len = expected.Length;
2548 for(int i=0; i< len; i++)
2549 Expect (expected[i]);
2552 // Does not consume the first non-whitespace character.
2553 private void SkipWhitespace ()
2555 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
2556 while (XmlConstructs.IsSpace (PeekChar ()))
2560 private bool ReadWhitespace ()
2563 int ch = PeekChar ();
2565 AppendValueChar (ReadChar ());
2566 } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
2568 if (ch != -1 && ch != '<')
2571 SetProperties (XmlNodeType.Whitespace,
2574 CreateValueString (),
2577 return (PeekChar () != -1);
2580 // read entity reference from attribute string and if parsable then return the value.
2581 private string ReadAttributeValueReference ()
2583 int endEntityPosition = attributeString.IndexOf(';',
2585 string entityName = attributeString.Substring (attributeValuePos + 1,
2586 endEntityPosition - attributeValuePos - 1);
2588 attributeValuePos = endEntityPosition + 1;
2590 if(entityName [0] == '#') {
2593 if(entityName [1] == 'x') {
2595 c = (char) int.Parse ("0" + entityName.Substring (2),
2596 System.Globalization.NumberStyles.HexNumber);
2599 c = (char) int.Parse (entityName.Substring (1));
2601 return c.ToString();
2606 case "lt": return "<";
2607 case "gt": return ">";
2608 case "amp": return "&";
2609 case "quot": return "\"";
2610 case "apos": return "'";
2611 default: return null;
2616 private string UnescapeAttributeValue (string unresolved)
2618 if(unresolved == null) return null;
2620 // trim start/end edge of quotation character.
2621 return Dereference (unresolved.Substring (1, unresolved.Length - 2));
2624 private string Dereference (string unresolved)
2626 StringBuilder resolved = new StringBuilder();
2628 int next = unresolved.IndexOf ('&');
2634 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
2635 int endPos = unresolved.IndexOf (';', next+1);
2637 unresolved.Substring (next + 1, endPos - next - 1);
2638 if(entityName [0] == '#') {
2641 if(entityName [1] == 'x') {
2643 c = (char) int.Parse ("0" + entityName.Substring (2),
2644 System.Globalization.NumberStyles.HexNumber);
2647 c = (char) int.Parse (entityName.Substring (1));
2649 resolved.Append (c);
2651 switch(entityName) {
2652 case "lt": resolved.Append ("<"); break;
2653 case "gt": resolved.Append (">"); break;
2654 case "amp": resolved.Append ("&"); break;
2655 case "quot": resolved.Append ("\""); break;
2656 case "apos": resolved.Append ("'"); break;
2657 // With respect to "Value", MS document is helpless
2658 // and the implemention returns inconsistent value
2659 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
2660 default: resolved.Append ("&" + entityName + ";"); break;
2664 if(pos > unresolved.Length)
2666 next = unresolved.IndexOf('&', pos);
2668 resolved.Append (unresolved.Substring(pos));
2670 return resolved.ToString();