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.
15 // I only checked with W3C test suite: http://www.w3.org/XML/Test/
16 // and libxml2 test cases.
18 // More strict well-formedness checking should be done.
20 // NameTables aren't being used completely yet.
22 // Some thought needs to be given to performance. There's too many
23 // strings being allocated.
25 // Some of the MoveTo methods haven't been implemented yet.
27 // xml:space, xml:lang aren't being tracked.
31 using System.Collections;
35 using Mono.Xml.Native;
39 public class XmlTextReader : XmlReader, IXmlLineInfo
41 WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
44 protected XmlTextReader ()
48 public XmlTextReader (Stream input)
49 : this (new XmlStreamReader (input))
53 public XmlTextReader (string url)
54 : this(url, new NameTable ())
58 public XmlTextReader (TextReader input)
59 : this (input, new NameTable ())
63 protected XmlTextReader (XmlNameTable nt)
64 : this (String.Empty, null, XmlNodeType.None, null)
68 public XmlTextReader (Stream input, XmlNameTable nt)
69 : this(new XmlStreamReader (input), nt)
73 public XmlTextReader (string url, Stream input)
74 : this (url, new XmlStreamReader (input))
78 public XmlTextReader (string url, TextReader input)
79 : this (url, input, new NameTable ())
83 [MonoTODO("Non-filename-url must be supported. Waiting for WebClient")]
84 public XmlTextReader (string url, XmlNameTable nt)
85 : this (url, new XmlStreamReader (url), nt)
89 public XmlTextReader (TextReader input, XmlNameTable nt)
90 : this (String.Empty, input, nt)
94 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
95 : this (context.BaseURI, new XmlStreamReader (xmlFragment), fragType, context)
99 public XmlTextReader (string url, Stream input, XmlNameTable nt)
100 : this (url, new XmlStreamReader (input), nt)
104 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
105 : this (url, input, XmlNodeType.Document, null)
109 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
110 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
111 : this (context.BaseURI, new StringReader (xmlFragment), fragType, context)
115 // TODO still remains as described at head of this file,
116 // but it might not be TODO of the constructors...
117 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
119 this.Initialize (url, context, fragment, fragType);
126 public override int AttributeCount
128 get { return attributes.Count; }
131 public override string BaseURI
133 get { return parserContext.BaseURI; }
136 public override int Depth
143 public Encoding Encoding
145 get { return parserContext.Encoding; }
148 public override bool EOF
153 readState == ReadState.EndOfFile ||
154 readState == ReadState.Closed;
158 public override bool HasValue
160 get { return value != String.Empty; }
163 public override bool IsDefault
167 // XmlTextReader does not expand default attributes.
172 public override bool IsEmptyElement
174 get { return isEmptyElement; }
177 public override string this [int i]
179 get { return GetAttribute (i); }
182 public override string this [string name]
184 get { return GetAttribute (name); }
187 public override string this [string localName, string namespaceName]
189 get { return GetAttribute (localName, namespaceName); }
192 public int LineNumber
194 get { return currentInput.LineNumber; }
197 public int LinePosition
199 get { return currentInput.LinePosition; }
202 public override string LocalName
204 get { return localName; }
207 public override string Name
212 public bool Namespaces
214 get { return namespaces; }
216 if (readState != ReadState.Initial)
217 throw new InvalidOperationException ("Namespaces have to be set before reading.");
222 public override string NamespaceURI
224 get { return namespaceURI; }
227 public override XmlNameTable NameTable
229 get { return parserContext.NameTable; }
232 public override XmlNodeType NodeType
234 get { return nodeType; }
238 public bool Normalization
240 get { throw new NotImplementedException (); }
241 set { throw new NotImplementedException (); }
244 public override string Prefix
246 get { return prefix; }
249 public override char QuoteChar
252 // value string holds attribute quotation char.
253 if (NodeType == XmlNodeType.Attribute)
260 public override ReadState ReadState
262 get { return readState; }
265 public override string Value
268 if(NodeType == XmlNodeType.Attribute)
269 return UnescapeAttributeValue(value);
275 public WhitespaceHandling WhitespaceHandling
277 get { return whitespaceHandling; }
278 set { whitespaceHandling = value; }
282 public override string XmlLang
284 get { throw new NotImplementedException (); }
287 public XmlResolver XmlResolver
289 set { resolver = value; }
293 public override XmlSpace XmlSpace
295 get { throw new NotImplementedException (); }
303 public override void Close ()
305 readState = ReadState.Closed;
308 public override string GetAttribute (int i)
310 if (i > attributes.Count)
311 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
313 return UnescapeAttributeValue (attributes [orderedAttributes [i]] as string);
316 public override string GetAttribute (string name)
318 return attributes.ContainsKey (name) ?
319 UnescapeAttributeValue (attributes [name] as string) : String.Empty;
322 public override string GetAttribute (string localName, string namespaceURI)
324 foreach (DictionaryEntry entry in attributes)
326 string thisName = entry.Key as string;
328 int indexOfColon = thisName.IndexOf (':');
330 if (indexOfColon != -1) {
331 string thisLocalName = thisName.Substring (indexOfColon + 1);
333 if (localName == thisLocalName) {
334 string thisPrefix = thisName.Substring (0, indexOfColon);
335 string thisNamespaceURI = LookupNamespace (thisPrefix);
337 if (namespaceURI == thisNamespaceURI)
338 return attributes.ContainsKey (thisName) ?
339 UnescapeAttributeValue (attributes [thisName] as string) : String.Empty;
341 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
342 return attributes.ContainsKey (thisName) ?
343 UnescapeAttributeValue (attributes [thisName] as string) : String.Empty;
350 public TextReader GetRemainder ()
352 throw new NotImplementedException ();
355 bool IXmlLineInfo.HasLineInfo ()
360 public override string LookupNamespace (string prefix)
362 return parserContext.NamespaceManager.LookupNamespace (prefix);
365 public override void MoveToAttribute (int i)
369 if (attributes == null || orderedAttributes.Count < i || i < 0)
370 throw new ArgumentOutOfRangeException ("attribute index out of range.");
372 string name = orderedAttributes [i] as string;
373 string value = attributes [name] as string;
375 XmlNodeType.Attribute, // nodeType
377 false, // isEmptyElement
379 false // clearAttributes
381 attributeValuePos = 0;
384 public override bool MoveToAttribute (string name)
389 if (attributes == null)
392 if (orderedAttributesEnumerator == null) {
394 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
397 while (orderedAttributesEnumerator.MoveNext ()) {
398 if(name == orderedAttributesEnumerator.Current as string) {
405 string value = attributes [name] as string;
407 XmlNodeType.Attribute, // nodeType
409 false, // isEmptyElement
411 false // clearAttributes
413 attributeValuePos = 0;
420 public override bool MoveToAttribute (string localName, string namespaceName)
422 throw new NotImplementedException ();
425 public override bool MoveToElement ()
427 if (orderedAttributesEnumerator != null) {
428 orderedAttributesEnumerator = null;
429 RestoreProperties ();
436 public override bool MoveToFirstAttribute ()
439 return MoveToNextAttribute ();
442 public override bool MoveToNextAttribute ()
444 if (attributes == null)
447 if (orderedAttributesEnumerator == null) {
449 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
452 if (orderedAttributesEnumerator.MoveNext ()) {
453 string name = orderedAttributesEnumerator.Current as string;
454 string value = attributes [name] as string;
456 XmlNodeType.Attribute, // nodeType
458 false, // isEmptyElement
460 false // clearAttributes
462 attributeValuePos = 0;
469 public override bool Read ()
473 readState = ReadState.Interactive;
475 more = ReadContent ();
480 public override bool ReadAttributeValue ()
482 // 'attributeString' holds real string value (without their
483 // quotation characters).
485 // 'attributeValuePos' holds current position
486 // of 'attributeString' while iterating ReadAttribute().
488 // -1 if ReadAttributeValue() has already finished.
489 // 0 if ReadAttributeValue() ready to start reading.
490 // >0 if ReadAttributeValue() already got 1 or more values
492 // local 'refPosition' holds the position on the
493 // attributeString which may be used next time.
495 if (attributeValuePos < 0) {
496 SetProperties (XmlNodeType.None,
504 // If not started, then initialize attributeString when parsing is at start.
505 if (attributeValuePos == 0)
507 value.Substring (1, value.Length - 2);
509 returnEntityReference = false;
510 value = String.Empty;
515 refPosition = attributeString.IndexOf ('&', attributeValuePos);
516 if (refPosition < 0) {
517 // Reached to the end of value string.
518 value += attributeString.Substring (attributeValuePos);
519 attributeValuePos = -1;
521 } else if (refPosition == attributeValuePos) {
522 string parsed = ReadAttributeValueReference ();
526 // Found that an entity reference starts from this point.
527 // reset position to after '&'.
528 attributeValuePos = refPosition;
529 if (value.Length <= 0) {
530 int endNamePos = attributeString.IndexOf (";", attributeValuePos);
531 value = attributeString.Substring (attributeValuePos+1, endNamePos - attributeValuePos - 1);
532 attributeValuePos += value.Length + 2;
533 returnEntityReference = true;
538 value += attributeString.Substring (attributeValuePos,
539 refPosition - attributeValuePos);
540 attributeValuePos = refPosition;
543 } while (++loop > 0);
545 if (returnEntityReference)
546 SetProperties (XmlNodeType.EntityReference,
552 SetProperties (XmlNodeType.Text,
562 public int ReadBase64 (byte [] buffer, int offset, int length)
564 throw new NotImplementedException ();
568 public int ReadBinHex (byte [] buffer, int offset, int length)
570 throw new NotImplementedException ();
574 public int ReadChars (char [] buffer, int offset, int length)
576 throw new NotImplementedException ();
579 public override string ReadInnerXml ()
581 if (readState != ReadState.Interactive)
585 case XmlNodeType.Attribute:
586 return value.Substring (1, value.Length - 2);
587 case XmlNodeType.Element:
591 int startDepth = depth;
593 innerXmlBuilder.Length = 0;
596 if (NodeType != XmlNodeType.EndElement || depth + 1 > startDepth)
597 innerXmlBuilder.Append (currentTag);
598 } while (depth >= startDepth);
600 string xml = innerXmlBuilder.ToString ();
601 innerXmlBuilder.Length = 0;
603 case XmlNodeType.None:
604 // MS document is incorrect. Seems not to progress.
612 public override string ReadOuterXml ()
614 if (readState != ReadState.Interactive)
618 case XmlNodeType.Attribute:
619 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
620 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
621 case XmlNodeType.Element:
622 bool isEmpty = IsEmptyElement;
623 string startTag = currentTag.ToString ();
626 if (NodeType == XmlNodeType.Element && !isEmpty)
627 return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
629 return currentTag.ToString ();
630 case XmlNodeType.None:
631 // MS document is incorrect. Seems not to progress.
640 public override string ReadString ()
642 throw new NotImplementedException ();
646 public void ResetState ()
648 throw new NotImplementedException ();
651 public override void ResolveEntity ()
653 // XmlTextReaders don't resolve entities.
654 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
660 // Parsed DTD Objects
661 internal DTDObjectModel currentSubset;
663 internal void Initialize (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
665 parserContext = context;
666 if (context == null) {
667 XmlNameTable nt = new NameTable ();
668 parserContext = new XmlParserContext (nt,
669 new XmlNamespaceManager (nt),
673 if (url != null && url != String.Empty)
674 parserContext.BaseURI = url;
678 case XmlNodeType.Attribute:
681 case XmlNodeType.Element:
682 allowMultipleRoot = true;
684 case XmlNodeType.Document:
687 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
690 this.currentInput = new XmlParserInput (fragment, url);
691 StreamReader sr = fragment as StreamReader;
697 private XmlParserContext parserContext;
699 private XmlParserInput currentInput;
700 private Stack parserInputStack = new Stack ();
701 private ReadState readState;
704 private int elementDepth;
705 private bool depthDown;
707 private bool popScope;
708 private Stack elementStack;
709 private Stack baseURIStack;
710 private bool haveEnteredDocument;
711 private bool allowMultipleRoot = false;
713 private XmlNodeType nodeType;
715 private string prefix;
716 private string localName;
717 private string namespaceURI;
718 private bool isEmptyElement;
719 private string value;
721 private XmlNodeType saveNodeType;
722 private string saveName;
723 private string savePrefix;
724 private string saveLocalName;
725 private string saveNamespaceURI;
726 private bool saveIsEmptyElement;
728 private Hashtable attributes;
729 private ArrayList orderedAttributes;
730 private IEnumerator orderedAttributesEnumerator;
732 private bool returnEntityReference;
733 private string entityReferenceName;
735 private char [] nameBuffer;
736 private int nameLength;
737 private int nameCapacity;
738 private const int initialNameCapacity = 256;
740 private char [] valueBuffer;
741 private int valueLength;
742 private int valueCapacity;
743 private const int initialValueCapacity = 8192;
745 // A buffer for ReadContent for ReadOuterXml
746 private StringBuilder currentTag {
748 return currentInput.CurrentMarkup;
752 private string attributeString = String.Empty;
753 private int attributeValuePos;
754 // This should be only referenced(used) by ReadInnerXml(). Kind of flyweight pattern.
755 private StringBuilder innerXmlBuilder;
757 // Parameter entity placeholder
758 private Hashtable parameterEntities = new Hashtable ();
761 private XmlResolver resolver = new XmlUrlResolver ();
763 private bool namespaces = true;
765 private XmlException ReaderError (string message)
767 return new XmlException (message, LineNumber, LinePosition);
772 readState = ReadState.Initial;
778 elementStack = new Stack();
779 baseURIStack = new Stack();
780 haveEnteredDocument = false;
782 nodeType = XmlNodeType.None;
784 prefix = String.Empty;
785 localName = string.Empty;
786 isEmptyElement = false;
787 value = String.Empty;
789 attributes = new Hashtable ();
790 orderedAttributes = new ArrayList ();
791 orderedAttributesEnumerator = null;
793 returnEntityReference = false;
794 entityReferenceName = String.Empty;
796 nameBuffer = new char [initialNameCapacity];
798 nameCapacity = initialNameCapacity;
800 valueBuffer = new char [initialValueCapacity];
802 valueCapacity = initialValueCapacity;
804 innerXmlBuilder = new StringBuilder ();
807 // Use this method rather than setting the properties
808 // directly so that all the necessary properties can
809 // be changed in harmony with each other. Maybe the
810 // fields should be in a seperate class to help enforce
812 private void SetProperties (
813 XmlNodeType nodeType,
817 bool clearAttributes)
819 this.nodeType = nodeType;
821 this.isEmptyElement = isEmptyElement;
823 this.elementDepth = depth;
829 int indexOfColon = name.IndexOf (':');
831 if (indexOfColon == -1) {
832 prefix = String.Empty;
835 prefix = name.Substring (0, indexOfColon);
836 localName = name.Substring (indexOfColon + 1);
839 prefix = String.Empty;
843 namespaceURI = LookupNamespace (prefix);
846 private void SaveProperties ()
848 saveNodeType = nodeType;
851 saveLocalName = localName;
852 saveNamespaceURI = namespaceURI;
853 saveIsEmptyElement = isEmptyElement;
854 // An element's value is always String.Empty.
857 private void RestoreProperties ()
859 nodeType = saveNodeType;
862 localName = saveLocalName;
863 namespaceURI = saveNamespaceURI;
864 isEmptyElement = saveIsEmptyElement;
865 value = String.Empty;
868 private void AddAttribute (string name, string value)
870 attributes.Add (name, value);
871 orderedAttributes.Add (name);
874 private void ClearAttributes ()
876 if (attributes.Count > 0) {
878 orderedAttributes.Clear ();
881 orderedAttributesEnumerator = null;
884 private int PeekChar ()
886 return currentInput.PeekChar ();
889 private int ReadChar ()
891 return currentInput.ReadChar ();
894 // This should really keep track of some state so
895 // that it's not possible to have more than one document
896 // element or text outside of the document element.
897 private bool ReadContent ()
899 currentTag.Length = 0;
901 parserContext.NamespaceManager.PopScope ();
905 if (returnEntityReference) {
906 SetEntityReferenceProperties ();
908 switch (PeekChar ()) {
913 case '\r': goto case ' ';
914 case '\n': goto case ' ';
915 case '\t': goto case ' ';
917 if (whitespaceHandling == WhitespaceHandling.All ||
918 whitespaceHandling == WhitespaceHandling.Significant)
919 return ReadWhitespace ();
922 return ReadContent ();
925 throw new XmlException ("unexpected end of file. Current depth is " + depth);
926 readState = ReadState.EndOfFile;
928 XmlNodeType.None, // nodeType
929 String.Empty, // name
930 false, // isEmptyElement
931 String.Empty, // value
932 true // clearAttributes
940 return this.ReadState != ReadState.EndOfFile;
943 private void SetEntityReferenceProperties ()
946 XmlNodeType.EntityReference, // nodeType
947 entityReferenceName, // name
948 false, // isEmptyElement
949 String.Empty, // value
950 true // clearAttributes
953 returnEntityReference = false;
954 entityReferenceName = String.Empty;
957 // The leading '<' has already been consumed.
958 private void ReadTag ()
968 ReadProcessingInstruction ();
980 // The leading '<' has already been consumed.
981 private void ReadStartTag ()
983 parserContext.NamespaceManager.PushScope ();
985 string name = ReadName ();
986 if (haveEnteredDocument && elementStack.Count == 0 && !allowMultipleRoot)
987 throw ReaderError("document has terminated, cannot open new element");
989 haveEnteredDocument = true;
992 bool isEmptyElement = false;
996 if (XmlConstructs.IsNameStart (PeekChar ()))
999 if (PeekChar () == '/') {
1001 isEmptyElement = true;
1006 elementStack.Push (name);
1007 baseURIStack.Push (attributes ["xml:base"] != null ?
1008 attributes ["xml:base"] : BaseURI);
1014 XmlNodeType.Element, // nodeType
1016 isEmptyElement, // isEmptyElement
1017 String.Empty, // value
1018 false // clearAttributes
1028 // The reader is positioned on the first character
1029 // of the element's name.
1030 private void ReadEndTag ()
1032 string name = ReadName ();
1033 if (elementStack.Count == 0)
1034 throw ReaderError("closing element without matching opening element");
1035 if ((string)elementStack.Pop() != name)
1036 throw ReaderError("unmatched closing element");
1037 baseURIStack.Pop ();
1045 XmlNodeType.EndElement, // nodeType
1047 false, // isEmptyElement
1048 String.Empty, // value
1049 true // clearAttributes
1055 private void AppendNameChar (int ch)
1057 CheckNameCapacity ();
1058 nameBuffer [nameLength++] = (char)ch;
1061 private void CheckNameCapacity ()
1063 if (nameLength == nameCapacity) {
1064 nameCapacity = nameCapacity * 2;
1065 char [] oldNameBuffer = nameBuffer;
1066 nameBuffer = new char [nameCapacity];
1067 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1071 private string CreateNameString ()
1073 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1076 private void AppendValueChar (int ch)
1078 CheckValueCapacity ();
1079 valueBuffer [valueLength++] = (char)ch;
1082 private void CheckValueCapacity ()
1084 if (valueLength == valueCapacity) {
1085 valueCapacity = valueCapacity * 2;
1086 char [] oldValueBuffer = valueBuffer;
1087 valueBuffer = new char [valueCapacity];
1088 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1092 private string CreateValueString ()
1094 return new String (valueBuffer, 0, valueLength);
1097 // The reader is positioned on the first character
1099 private void ReadText (bool cleanValue)
1104 int ch = PeekChar ();
1106 while (ch != '<' && ch != -1) {
1109 if (ReadReference (false))
1112 AppendValueChar (ReadChar ());
1117 if (returnEntityReference && valueLength == 0) {
1118 SetEntityReferenceProperties ();
1121 XmlNodeType.Text, // nodeType
1122 String.Empty, // name
1123 false, // isEmptyElement
1124 CreateValueString (), // value
1125 true // clearAttributes
1130 // The leading '&' has already been consumed.
1131 // Returns true if the entity reference isn't a simple
1132 // character reference or one of the predefined entities.
1133 // This allows the ReadText method to break so that the
1134 // next call to Read will return the EntityReference node.
1135 private bool ReadReference (bool ignoreEntityReferences)
1137 if (PeekChar () == '#') {
1139 ReadCharacterReference ();
1141 ReadEntityReference (ignoreEntityReferences);
1143 return returnEntityReference;
1146 private void ReadCharacterReference ()
1150 if (PeekChar () == 'x') {
1153 while (PeekChar () != ';' && PeekChar () != -1) {
1154 int ch = ReadChar ();
1156 if (ch >= '0' && ch <= '9')
1157 value = (value << 4) + ch - '0';
1158 else if (ch >= 'A' && ch <= 'F')
1159 value = (value << 4) + ch - 'A' + 10;
1160 else if (ch >= 'a' && ch <= 'f')
1161 value = (value << 4) + ch - 'a' + 10;
1165 "invalid hexadecimal digit: {0} (#x{1:X})",
1170 while (PeekChar () != ';' && PeekChar () != -1) {
1171 int ch = ReadChar ();
1173 if (ch >= '0' && ch <= '9')
1174 value = value * 10 + ch - '0';
1178 "invalid decimal digit: {0} (#x{1:X})",
1186 AppendValueChar (value);
1189 private void ReadEntityReference (bool ignoreEntityReferences)
1193 int ch = PeekChar ();
1195 while (ch != ';' && ch != -1) {
1196 AppendNameChar (ReadChar ());
1202 string name = CreateNameString ();
1207 AppendValueChar ('<');
1210 AppendValueChar ('>');
1213 AppendValueChar ('&');
1216 AppendValueChar ('\'');
1219 AppendValueChar ('"');
1222 if (ignoreEntityReferences) {
1223 AppendValueChar ('&');
1225 foreach (char ch2 in name) {
1226 AppendValueChar (ch2);
1229 AppendValueChar (';');
1231 returnEntityReference = true;
1232 entityReferenceName = name;
1238 // The reader is positioned on the first character of
1239 // the attribute name.
1240 private void ReadAttributes ()
1243 string name = ReadName ();
1247 string value = ReadAttribute ();
1250 if (name == "xmlns")
1251 parserContext.NamespaceManager.AddNamespace (String.Empty, UnescapeAttributeValue (value));
1252 else if (name.StartsWith ("xmlns:"))
1253 parserContext.NamespaceManager.AddNamespace (name.Substring (6), UnescapeAttributeValue (value));
1255 AddAttribute (name, value);
1256 } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1259 // The reader is positioned on the quote character.
1260 // *Keeps quote char* to value to get_QuoteChar() correctly.
1261 private string ReadAttribute ()
1265 int quoteChar = ReadChar ();
1267 if (quoteChar != '\'' && quoteChar != '\"')
1268 throw ReaderError ("an attribute value was not quoted");
1270 AppendValueChar (quoteChar);
1272 while (PeekChar () != quoteChar) {
1273 int ch = ReadChar ();
1278 throw ReaderError ("attribute values cannot contain '<'");
1280 throw ReaderError ("unexpected end of file in an attribute value");
1282 AppendValueChar (ch);
1287 ReadChar (); // quoteChar
1288 AppendValueChar (quoteChar);
1290 return CreateValueString ();
1293 // The reader is positioned on the first character
1296 // Now it also reads XmlDeclaration, this method name became improper...
1297 private void ReadProcessingInstruction ()
1299 string target = ReadName ();
1304 while (PeekChar () != -1) {
1305 int ch = ReadChar ();
1307 if (ch == '?' && PeekChar () == '>') {
1312 AppendValueChar ((char)ch);
1317 XmlNodeType.XmlDeclaration :
1318 XmlNodeType.ProcessingInstruction, // nodeType
1320 false, // isEmptyElement
1321 CreateValueString (), // value
1322 true // clearAttributes
1326 // The reader is positioned on the first character after
1327 // the leading '<!'.
1328 private void ReadDeclaration ()
1330 int ch = PeekChar ();
1350 // The reader is positioned on the first character after
1351 // the leading '<!--'.
1352 private void ReadComment ()
1356 while (PeekChar () != -1) {
1357 int ch = ReadChar ();
1359 if (ch == '-' && PeekChar () == '-') {
1362 if (PeekChar () != '>')
1363 throw ReaderError ("comments cannot contain '--'");
1369 AppendValueChar ((char)ch);
1373 XmlNodeType.Comment, // nodeType
1374 String.Empty, // name
1375 false, // isEmptyElement
1376 CreateValueString (), // value
1377 true // clearAttributes
1381 // The reader is positioned on the first character after
1382 // the leading '<![CDATA['.
1383 private void ReadCDATA ()
1389 while (PeekChar () != -1) {
1394 if (ch == ']' && PeekChar () == ']') {
1395 ch = ReadChar (); // ']'
1397 if (PeekChar () == '>') {
1402 // AppendValueChar (']');
1403 // AppendValueChar (']');
1404 // ch = ReadChar ();
1408 AppendValueChar ((char)ch);
1412 XmlNodeType.CDATA, // nodeType
1413 String.Empty, // name
1414 false, // isEmptyElement
1415 CreateValueString (), // value
1416 true // clearAttributes
1420 // The reader is positioned on the first character after
1421 // the leading '<!DOCTYPE'.
1422 private void ReadDoctypeDecl ()
1424 string doctypeName = null;
1425 string publicId = String.Empty;
1426 string systemId = String.Empty;
1427 int intSubsetStartLine = 0;
1428 int intSubsetStartColumn = 0;
1431 doctypeName = ReadName ();
1436 systemId = ReadSystemLiteral (true);
1439 publicId = ReadPubidLiteral ();
1441 systemId = ReadSystemLiteral (false);
1447 if(PeekChar () == '[')
1449 // read markupdecl etc. or end of decl
1451 intSubsetStartLine = this.LineNumber;
1452 intSubsetStartColumn = this.LinePosition;
1453 int startPos = currentTag.Length;
1455 ReadInternalSubset ();
1456 // } while (nodeType != XmlNodeType.None);
1457 int endPos = currentTag.Length - 1;
1458 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1460 // end of DOCTYPE decl.
1465 currentSubset = new DTDObjectModel (); // merges both internal and external subsets in the meantime,
1466 int originalParserDepth = parserInputStack.Count;
1467 if (intSubsetStartLine > 0) {
1468 XmlParserInput original = currentInput;
1469 currentInput = new XmlParserInput (new StringReader (parserContext.InternalSubset), BaseURI, intSubsetStartLine, intSubsetStartColumn);
1471 CompileDTDSubset ();
1472 if (PeekChar () == -1 && parserInputStack.Count > 0)
1474 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth);
1475 if (dtdIncludeSect != 0)
1476 this.ReaderError ("INCLUDE section is not ended correctly.");
1477 currentInput = original;
1479 if (systemId != String.Empty) {
1480 pushParserInput (systemId);
1482 this.CompileDTDSubset ();
1483 if (PeekChar () == -1 && parserInputStack.Count > 1)
1485 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth + 1);
1489 // set properties for <!DOCTYPE> node
1491 XmlNodeType.DocumentType, // nodeType
1492 doctypeName, // name
1493 false, // isEmptyElement
1494 parserContext.InternalSubset, // value
1495 true // clearAttributes
1499 private void pushParserInput (string url)
1501 string absPath = null;
1504 Uri baseUrl = new Uri (BaseURI);
1505 absPath = resolver.ResolveUri (baseUrl, url).ToString ();
1506 } catch (UriFormatException) {
1507 if (Path.IsPathRooted (url))
1509 else if (BaseURI != String.Empty)
1510 absPath = new FileInfo (BaseURI).DirectoryName + Path.DirectorySeparatorChar + url;
1515 if (Path.IsPathRooted (url))
1517 else if (BaseURI != String.Empty)
1518 absPath = new FileInfo (BaseURI).DirectoryName + Path.DirectorySeparatorChar + url;
1522 foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1523 if (i.BaseURI == url)
1524 this.ReaderError ("Nested inclusion is not allowed: " + url);
1526 parserInputStack.Push (currentInput);
1527 currentInput = new XmlParserInput (new XmlStreamReader (absPath), absPath);
1528 baseURIStack.Push (BaseURI);
1529 parserContext.BaseURI = absPath;
1532 private void popParserInput ()
1534 currentInput = parserInputStack.Pop () as XmlParserInput;
1535 parserContext.BaseURI = this.baseURIStack.Pop () as string;
1538 private enum DtdInputState
\r
1547 InsideSingleQuoted,
\r
1548 InsideDoubleQuoted,
\r
1551 private class DtdInputStateStack
\r
1553 Stack intern = new Stack ();
\r
1554 public DtdInputStateStack ()
\r
1556 Push (DtdInputState.Free);
\r
1559 public DtdInputState Peek ()
\r
1561 return (DtdInputState) intern.Peek ();
\r
1564 public DtdInputState Pop ()
\r
1566 return (DtdInputState) intern.Pop ();
\r
1569 public void Push (DtdInputState val)
\r
1571 intern.Push (val);
\r
1576 DtdInputStateStack stateStack = new DtdInputStateStack ();
\r
1577 DtdInputState State {
\r
1578 get { return stateStack.Peek (); }
\r
1581 // Simply read but not generate any result.
1582 private void ReadInternalSubset ()
\r
1584 bool continueParse = true;
\r
1586 while (continueParse) {
\r
1587 switch (ReadChar ()) {
\r
1590 case DtdInputState.Free:
\r
1591 continueParse = false;
\r
1593 case DtdInputState.InsideDoubleQuoted:
\r
1595 case DtdInputState.InsideSingleQuoted:
\r
1598 throw ReaderError ("unexpected end of file at DTD.");
\r
1602 throw ReaderError ("unexpected end of file at DTD.");
\r
1604 if (State == DtdInputState.InsideDoubleQuoted ||
\r
1605 State == DtdInputState.InsideSingleQuoted)
\r
1606 continue; // well-formed
\r
1607 switch (ReadChar ()) {
\r
1609 stateStack.Push (DtdInputState.PI);
\r
1612 switch (ReadChar ()) {
\r
1614 switch (ReadChar ()) {
\r
1617 stateStack.Push (DtdInputState.ElementDecl);
\r
1621 stateStack.Push (DtdInputState.EntityDecl);
\r
1624 throw ReaderError ("unexpected token '<!E'.");
\r
1628 Expect ("TTLIST");
\r
1629 stateStack.Push (DtdInputState.AttlistDecl);
\r
1632 Expect ("OTATION");
\r
1633 stateStack.Push (DtdInputState.NotationDecl);
\r
1637 stateStack.Push (DtdInputState.Comment);
\r
1642 throw ReaderError ("unexpected '>'.");
\r
1646 if (State == DtdInputState.InsideSingleQuoted)
\r
1647 stateStack.Pop ();
\r
1648 else if (State != DtdInputState.Comment)
\r
1649 stateStack.Push (DtdInputState.InsideSingleQuoted);
\r
1652 if (State == DtdInputState.InsideDoubleQuoted)
\r
1653 stateStack.Pop ();
\r
1654 else if (State != DtdInputState.Comment)
\r
1655 stateStack.Push (DtdInputState.InsideDoubleQuoted);
\r
1659 case DtdInputState.ElementDecl:
\r
1660 goto case DtdInputState.NotationDecl;
\r
1661 case DtdInputState.AttlistDecl:
\r
1662 goto case DtdInputState.NotationDecl;
\r
1663 case DtdInputState.EntityDecl:
\r
1664 goto case DtdInputState.NotationDecl;
\r
1665 case DtdInputState.NotationDecl:
\r
1666 stateStack.Pop ();
\r
1668 case DtdInputState.InsideDoubleQuoted:
\r
1670 case DtdInputState.InsideSingleQuoted:
\r
1671 continue; // well-formed
\r
1672 case DtdInputState.Comment:
\r
1675 throw ReaderError ("unexpected token '>'");
\r
1679 if (State == DtdInputState.PI) {
\r
1680 if (ReadChar () == '>')
\r
1681 stateStack.Pop ();
\r
1685 if (State == DtdInputState.Comment) {
\r
1686 if (PeekChar () == '-') {
\r
1689 stateStack.Pop ();
\r
1694 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
\r
1695 throw ReaderError ("Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
\r
1701 // Read any one of following:
1702 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1703 // PI, Comment, Parameter Entity, or doctype termination char(']')
1705 // returns a node of some nodeType or null, setting nodeType.
1706 // (if None then ']' was found.)
1707 private void CompileDTDSubset()
1713 nodeType = XmlNodeType.None;
1723 // Only read, no store.
1724 ReadProcessingInstruction ();
1727 CompileDeclaration ();
1730 throw ReaderError ("Syntax Error after '<' character.");
1740 throw ReaderError (String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
1744 private void CompileDeclaration ()
1746 nodeType = XmlNodeType.DocumentType; // Hack!!
1751 // Only read, no store.
1761 if (PeekChar () == '%') {
1763 if (!XmlConstructs.IsSpace (PeekChar ())) {
1766 // throw ReaderError ("expected whitespace between '%' and name.");
1770 if (XmlConstructs.IsName (PeekChar ()))
1771 ReadParameterEntityDecl ();
1773 throw ReaderError ("expected name character");
1777 DTDEntityDeclaration ent = ReadEntityDecl ();
1778 if (currentSubset.EntityDecls [ent.Name] == null)
1779 currentSubset.EntityDecls.Add (ent.Name, ent);
1783 DTDElementDeclaration el = ReadElementDecl ();
1784 currentSubset.ElementDecls.Add (el.Name, el);
1787 throw ReaderError ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1792 DTDAttListDeclaration atl = ReadAttListDecl ();
1793 if (currentSubset.AttListDecls.ContainsKey (atl.Name))
1794 currentSubset.AttListDecls.Add (atl.Name, atl);
1798 DTDNotationDeclaration not = ReadNotationDecl ();
1799 currentSubset.NotationDecls.Add (not.Name, not);
1802 // conditional sections
1807 switch (ReadChar ()) {
1821 throw ReaderError ("Syntax Error after '<!' characters.");
1825 private void ReadIgnoreSect ()
1830 int dtdIgnoreSect = 1;
1831 while (dtdIgnoreSect > 0) {
1832 switch (skip ? PeekChar () : ReadChar ()) {
1834 throw ReaderError ("Unexpected IGNORE section end.");
1837 if (ReadChar () == '!' && ReadChar () == '[')
1841 if (ReadChar () == ']') {
1842 if (ReadChar () == '>')
1853 // The reader is positioned on the head of the name.
\r
1854 private DTDElementDeclaration ReadElementDecl ()
\r
1856 DTDElementDeclaration decl = new DTDElementDeclaration ();
\r
1857 SkipWhitespace ();
\r
1858 TryExpandPERef ();
\r
1859 decl.Name = ReadName ();
\r
1860 SkipWhitespace ();
\r
1861 TryExpandPERef ();
\r
1862 ReadContentSpec (decl);
\r
1863 SkipWhitespace ();
\r
1868 // read 'children'(BNF) of contentspec
\r
1869 private void ReadContentSpec (DTDElementDeclaration decl)
\r
1871 switch(PeekChar ())
\r
1874 decl.IsEmpty = true;
\r
1878 decl.IsAny = true;
\r
1882 DTDContentModel model = decl.ContentModel;
\r
1884 SkipWhitespace ();
\r
1885 TryExpandPERef ();
\r
1886 if(PeekChar () == '#') {
\r
1888 decl.IsMixedContent = true;
\r
1889 Expect ("#PCDATA");
\r
1890 SkipWhitespace ();
\r
1891 TryExpandPERef ();
\r
1892 SkipWhitespace ();
\r
1893 while(PeekChar () != ')') {
\r
1895 SkipWhitespace ();
\r
1896 TryExpandPERef ();
\r
1897 SkipWhitespace ();
\r
1898 model.ChildModels.Add (ReadName ());
\r
1899 SkipWhitespace ();
\r
1900 TryExpandPERef ();
\r
1903 if(PeekChar () == '*')
\r
1904 ReadChar (); // ZeroOrMore
\r
1906 // Non-Mixed Contents
\r
1907 model.ChildModels.Add (ReadCP ());
\r
1908 SkipWhitespace ();
\r
1910 do { // copied from ReadCP() ...;-)
\r
1911 TryExpandPERef ();
\r
1912 SkipWhitespace ();
\r
1913 if(PeekChar ()=='|') {
\r
1915 model.OrderType = DTDContentOrderType.Or;
\r
1917 SkipWhitespace ();
\r
1918 model.ChildModels.Add (ReadCP ());
\r
1919 SkipWhitespace ();
\r
1921 else if(PeekChar () == ',')
\r
1924 model.OrderType = DTDContentOrderType.Seq;
\r
1926 SkipWhitespace ();
\r
1927 model.ChildModels.Add (ReadCP ());
\r
1928 SkipWhitespace ();
\r
1936 switch(PeekChar ())
\r
1939 model.MinOccurs = 0;
\r
1943 model.MinOccurs = 0;
\r
1944 model.MaxOccurs = decimal.MaxValue;
\r
1948 model.MaxOccurs = decimal.MaxValue;
\r
1952 SkipWhitespace ();
\r
1954 SkipWhitespace ();
\r
1959 // Read 'cp' (BNF) of contentdecl (BNF)
\r
1960 private DTDContentModel ReadCP ()
\r
1962 DTDContentModel model = new DTDContentModel ();
\r
1963 TryExpandPERef ();
\r
1964 if(PeekChar () == '(') {
\r
1966 SkipWhitespace ();
\r
1967 model.ChildModels.Add (ReadCP ());
\r
1968 SkipWhitespace ();
\r
1970 TryExpandPERef ();
\r
1971 SkipWhitespace ();
\r
1972 if(PeekChar ()=='|') {
\r
1974 model.OrderType = DTDContentOrderType.Or;
\r
1976 SkipWhitespace ();
\r
1977 model.ChildModels.Add (ReadCP ());
\r
1978 SkipWhitespace ();
\r
1980 else if(PeekChar () == ',') {
\r
1982 model.OrderType = DTDContentOrderType.Seq;
\r
1984 SkipWhitespace ();
\r
1985 model.ChildModels.Add (ReadCP ());
\r
1986 SkipWhitespace ();
\r
1992 SkipWhitespace ();
\r
1996 TryExpandPERef ();
\r
1997 model.ElementName = ReadName ();
\r
2000 switch(PeekChar ()) {
\r
2002 model.MinOccurs = 0;
\r
2006 model.MinOccurs = 0;
\r
2007 model.MaxOccurs = decimal.MaxValue;
\r
2011 model.MaxOccurs = decimal.MaxValue;
\r
2018 // The reader is positioned on the first name char.
\r
2019 private void ReadParameterEntityDecl ()
\r
2021 DTDParameterEntityDeclaration decl =
\r
2022 new DTDParameterEntityDeclaration();
\r
2023 decl.BaseURI = BaseURI;
\r
2025 decl.Name = ReadName ();
\r
2026 SkipWhitespace ();
\r
2028 if (PeekChar () == 'S' || PeekChar () == 'P') {
\r
2029 // throw new NotImplementedException ("External parameter entity reference is not implemented yet.");
\r
2030 // read publicId/systemId
\r
2031 ReadExternalID ();
\r
2032 decl.PublicId = attributes ["PUBLIC"] as string;
\r
2033 decl.SystemId = attributes ["SYSTEM"] as string;
\r
2034 SkipWhitespace ();
\r
2037 TryExpandPERef ();
\r
2038 int quoteChar = ReadChar ();
\r
2039 int start = currentTag.Length;
\r
2041 SkipWhitespace ();
\r
2042 int c = PeekChar ();
\r
2043 if ((int) c == -1)
\r
2044 throw new XmlException ("unexpected end of stream in entity value definition.");
\r
2048 if (quoteChar == '"') goto SKIP;
\r
2052 if (quoteChar == '\'') goto SKIP;
\r
2063 decl.Value = currentTag.ToString (start, currentTag.Length - start - 1);
\r
2065 SkipWhitespace ();
\r
2067 if (parameterEntities [decl.Name] == null) {
\r
2068 parameterEntities.Add (decl.Name, decl);
\r
2072 // reader is positioned on '%'
\r
2073 private void ImportAsPERef ()
\r
2075 StringBuilder sb = null;
\r
2076 int peRefStart = currentTag.Length;
\r
2077 string appendStr = "";
\r
2079 string peName = ReadName ();
\r
2081 DTDParameterEntityDeclaration peDecl =
\r
2082 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
\r
2083 if (peDecl == null)
\r
2084 throw ReaderError ("Parameter entity " + peName + " not found.");
\r
2085 if (peDecl.SystemId != null) {
\r
2086 pushParserInput (peDecl.SystemId);
\r
2088 sb = new StringBuilder ();
\r
2091 while (PeekChar () != -1)
\r
2092 sb.Append (ReadChar ());
\r
2093 popParserInput ();
\r
2094 appendStr = sb.ToString ();
\r
2096 appendStr = peDecl.Value;
\r
2098 currentTag.Remove (peRefStart,
\r
2099 currentTag.Length - peRefStart);
\r
2100 currentTag.Append (Dereference (appendStr));
\r
2103 // The reader is positioned on the head of the name.
\r
2104 private DTDEntityDeclaration ReadEntityDecl ()
\r
2106 DTDEntityDeclaration decl = new DTDEntityDeclaration ();
\r
2107 decl.Name = ReadName ();
\r
2108 SkipWhitespace ();
\r
2109 TryExpandPERef ();
\r
2110 SkipWhitespace ();
\r
2112 if (PeekChar () == 'S' || PeekChar () == 'P') {
\r
2113 // external entity
\r
2114 ReadExternalID ();
\r
2115 decl.PublicId = attributes ["PUBLIC"] as string;
\r
2116 decl.SystemId = attributes ["SYSTEM"] as string;
\r
2117 SkipWhitespace ();
\r
2118 if (PeekChar () == 'N')
\r
2122 SkipWhitespace ();
\r
2123 decl.NotationName = ReadName (); // ndata_name
\r
2128 decl.EntityValue = ReadEntityValueDecl ();
\r
2130 SkipWhitespace ();
\r
2135 private string ReadEntityValueDecl ()
\r
2137 SkipWhitespace ();
\r
2138 // quotation char will be finally removed on unescaping
\r
2139 int quoteChar = ReadChar ();
\r
2140 int start = currentTag.Length;
\r
2141 if (quoteChar != '\'' && quoteChar != '"')
\r
2142 throw new XmlException ("quotation char was expected.");
\r
2144 while (PeekChar () != quoteChar) {
\r
2145 switch (PeekChar ()) {
\r
2147 this.ImportAsPERef ();
\r
2152 // ReadCharacterReference ();
\r
2153 ReadReference (true);
\r
2156 throw new XmlException ("unexpected end of stream.");
\r
2162 string value = Dereference (currentTag.ToString (start, currentTag.Length - start));
\r
2163 Expect (quoteChar);
\r
2167 private DTDAttListDeclaration ReadAttListDecl ()
\r
2169 SkipWhitespace ();
\r
2170 TryExpandPERef ();
\r
2171 string name = ReadName (); // target element name
\r
2172 DTDAttListDeclaration decl =
\r
2173 currentSubset.AttListDecls [name] as DTDAttListDeclaration;
\r
2175 decl = new DTDAttListDeclaration ();
\r
2178 SkipWhitespace ();
\r
2179 TryExpandPERef ();
\r
2180 SkipWhitespace ();
\r
2182 while (XmlConstructs.IsName ((char) PeekChar ())) {
\r
2183 DTDAttributeDefinition def = ReadAttributeDefinition ();
\r
2184 if (decl.AttributeDefinitions [def.Name] == null)
\r
2185 decl.AttributeDefinitions.Add (def.Name, def);
\r
2186 SkipWhitespace ();
\r
2187 TryExpandPERef ();
\r
2188 SkipWhitespace ();
\r
2190 SkipWhitespace ();
\r
2195 private DTDAttributeDefinition ReadAttributeDefinition ()
\r
2197 DTDAttributeDefinition def = new DTDAttributeDefinition ();
\r
2200 TryExpandPERef ();
\r
2201 def.Name = ReadName ();
\r
2202 SkipWhitespace ();
\r
2205 TryExpandPERef ();
\r
2206 switch(PeekChar ()) {
\r
2207 case 'C': // CDATA
\r
2209 def.AttributeType = DTDAttributeType.CData;
\r
2211 case 'I': // ID, IDREF, IDREFS
\r
2213 if(PeekChar () == 'R') {
\r
2215 if(PeekChar () == 'S') {
\r
2218 def.AttributeType = DTDAttributeType.IdRefs;
\r
2221 def.AttributeType = DTDAttributeType.IdRef;
\r
2224 def.AttributeType = DTDAttributeType.Id;
\r
2226 case 'E': // ENTITY, ENTITIES
\r
2228 switch(ReadChar ()) {
\r
2229 case 'Y': // ENTITY
\r
2230 def.AttributeType = DTDAttributeType.Entity;
\r
2232 case 'I': // ENTITIES
\r
2234 def.AttributeType = DTDAttributeType.Entities;
\r
2238 case 'N': // NMTOKEN, NMTOKENS, NOTATION
\r
2240 switch(PeekChar ()) {
\r
2242 Expect ("MTOKEN");
\r
2243 if(PeekChar ()=='S') { // NMTOKENS
\r
2245 def.AttributeType = DTDAttributeType.NmTokens;
\r
2248 def.AttributeType = DTDAttributeType.NmToken;
\r
2251 Expect ("OTATION");
\r
2252 def.AttributeType = DTDAttributeType.Notation;
\r
2253 SkipWhitespace ();
\r
2255 SkipWhitespace ();
\r
2256 def.EnumeratedNotations.Add (ReadName ()); // notation name
\r
2257 SkipWhitespace ();
\r
2258 while(PeekChar () == '|') {
\r
2260 SkipWhitespace ();
\r
2261 def.EnumeratedNotations.Add (ReadName ()); // notation name
\r
2262 SkipWhitespace ();
\r
2267 throw new XmlException ("attribute declaration syntax error.");
\r
2270 default: // Enumerated Values
\r
2271 TryExpandPERef ();
\r
2273 SkipWhitespace ();
\r
2274 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
\r
2275 SkipWhitespace ();
\r
2276 while(PeekChar () == '|') {
\r
2278 SkipWhitespace ();
\r
2279 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
\r
2280 SkipWhitespace ();
\r
2285 SkipWhitespace ();
\r
2287 TryExpandPERef ();
\r
2290 if(PeekChar () == '#')
\r
2293 switch(PeekChar ())
\r
2296 Expect ("REQUIRED");
\r
2297 def.OccurenceType = DTDAttributeOccurenceType.Required;
\r
2300 Expect ("IMPLIED");
\r
2301 def.OccurenceType = DTDAttributeOccurenceType.Optional;
\r
2305 def.OccurenceType = DTDAttributeOccurenceType.Fixed;
\r
2306 SkipWhitespace ();
\r
2307 def.UnresolvedDefaultValue = ReadAttribute ();
\r
2311 // one of the enumerated value
\r
2312 if (PeekChar () == -1) {
\r
2313 popParserInput ();
\r
2315 SkipWhitespace ();
\r
2316 def.UnresolvedDefaultValue = ReadAttribute ();
\r
2322 private DTDNotationDeclaration ReadNotationDecl()
\r
2324 DTDNotationDeclaration decl = new DTDNotationDeclaration ();
\r
2325 SkipWhitespace ();
\r
2326 decl.Name = ReadName (); // notation name
\r
2327 if (namespaces) { // copy from SetProperties ;-)
2328 int indexOfColon = decl.Name.IndexOf (':');
2330 if (indexOfColon == -1) {
2331 decl.Prefix = String.Empty;
2332 decl.LocalName = decl.Name;
2334 decl.Prefix = decl.Name.Substring (0, indexOfColon);
2335 decl.LocalName = decl.Name.Substring (indexOfColon + 1);
2338 decl.Prefix = String.Empty;
2339 decl.LocalName = decl.Name;
2342 SkipWhitespace ();
\r
2343 if(PeekChar () == 'P')
\r
2345 decl.PublicId = ReadPubidLiteral ();
\r
2346 SkipWhitespace ();
\r
2348 SkipWhitespace ();
\r
2349 if(PeekChar () == 'S')
\r
2351 decl.SystemId = ReadSystemLiteral (true);
\r
2352 SkipWhitespace ();
\r
2354 if(decl.PublicId == null && decl.SystemId == null)
\r
2355 throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
\r
2360 private void TryExpandPERef ()
\r
2362 if (PeekChar () == '%') {
\r
2364 if (!XmlConstructs.IsName (PeekChar ()))
\r
2370 // reader is positioned on the first letter of the name.
\r
2371 private void ExpandPERef ()
\r
2373 ExpandPERef (true);
\r
2376 private void ExpandPERef (bool attachSpace)
\r
2378 string peName = ReadName ();
\r
2380 ExpandNamedPERef (peName, attachSpace);
\r
2383 private void ExpandNamedPERef (string peName, bool attachSpace)
\r
2385 DTDParameterEntityDeclaration decl =
\r
2386 parameterEntities [peName] as DTDParameterEntityDeclaration;
\r
2388 throw new XmlException ("undeclared parameter entity: '" + peName + "'");
\r
2389 if (decl.SystemId != null) {
\r
2390 pushParserInput (decl.SystemId);
\r
2394 currentInput.InsertParameterEntityBuffer (attachSpace ? " " + Dereference (decl.Value) + " " : decl.Value);
\r
2395 SkipWhitespace (); // is it ok?
\r
2396 // while (PeekChar () == '%')
\r
2397 // TryExpandPERef (); // recursive
\r
2400 private void ReadExternalID() {
\r
2401 switch(PeekChar ()) {
\r
2403 attributes ["PUBLIC"] = null;
\r
2404 attributes ["SYSTEM"] = ReadSystemLiteral (true);
\r
2407 attributes ["PUBLIC"] = ReadPubidLiteral ();
\r
2408 SkipWhitespace ();
\r
2409 attributes ["SYSTEM"] = ReadSystemLiteral (false);
\r
2414 // The reader is positioned on the first 'S' of "SYSTEM".
2415 private string ReadSystemLiteral (bool expectSYSTEM)
2420 int quoteChar = ReadChar (); // apos or quot
2421 int startPos = currentTag.Length;
2423 while(c != quoteChar) {
2425 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
2427 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2430 private string ReadPubidLiteral()
2434 int quoteChar = ReadChar ();
2435 int startPos = currentTag.Length;
2437 while(c != quoteChar)
2440 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
2441 if(c != quoteChar && !XmlConstructs.IsPubid (c))
2442 throw ReaderError("character '" + (char)c + "' not allowed for PUBLIC ID");
2444 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2447 // The reader is positioned on the first character
\r
2449 internal string ReadName ()
\r
2451 return ReadNameOrNmToken(false);
\r
2454 // The reader is positioned on the first character
\r
2456 private string ReadNmToken ()
\r
2458 return ReadNameOrNmToken(true);
\r
2461 private string ReadNameOrNmToken(bool isNameToken)
\r
2463 int ch = PeekChar ();
\r
2465 if (!XmlConstructs.IsName ((char) ch))
\r
2466 throw ReaderError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
\r
2469 if (!XmlConstructs.IsNameStart ((char) PeekChar ()))
\r
2470 throw ReaderError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
\r
2475 AppendNameChar (ReadChar ());
2477 while (XmlConstructs.IsName (PeekChar ())) {
2478 AppendNameChar (ReadChar ());
2481 return CreateNameString ();
2484 // Read the next character and compare it against the
2485 // specified character.
2486 private void Expect (int expected)
2488 int ch = ReadChar ();
2490 if (ch != expected) {
2493 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2501 private void Expect (string expected)
2503 int len = expected.Length;
2504 for(int i=0; i< len; i++)
2505 Expect (expected[i]);
2508 // Does not consume the first non-whitespace character.
2509 private void SkipWhitespace ()
2511 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
2512 while (XmlConstructs.IsSpace (PeekChar ()))
2516 private bool ReadWhitespace ()
2519 int ch = PeekChar ();
2521 AppendValueChar (ReadChar ());
2522 } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
2524 if (ch != -1 && ch != '<')
2527 SetProperties (XmlNodeType.Whitespace,
2530 CreateValueString (),
2533 return (PeekChar () != -1);
2536 // read entity reference from attribute string and if parsable then return the value.
2537 private string ReadAttributeValueReference ()
2539 int endEntityPosition = attributeString.IndexOf(';',
2541 string entityName = attributeString.Substring (attributeValuePos + 1,
2542 endEntityPosition - attributeValuePos - 1);
2544 attributeValuePos = endEntityPosition + 1;
2546 if(entityName [0] == '#') {
2549 if(entityName [1] == 'x') {
2551 c = (char) int.Parse ("0" + entityName.Substring (2),
2552 System.Globalization.NumberStyles.HexNumber);
2555 c = (char) int.Parse (entityName.Substring (1));
2557 return c.ToString();
2562 case "lt": return "<";
2563 case "gt": return ">";
2564 case "amp": return "&";
2565 case "quot": return "\"";
2566 case "apos": return "'";
2567 default: return null;
2572 private string UnescapeAttributeValue (string unresolved)
2574 if(unresolved == null) return null;
2576 // trim start/end edge of quotation character.
2577 return Dereference (unresolved.Substring (1, unresolved.Length - 2));
2580 private string Dereference (string unresolved)
2582 StringBuilder resolved = new StringBuilder();
2584 int next = unresolved.IndexOf ('&');
2590 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
2591 int endPos = unresolved.IndexOf (';', next+1);
2593 unresolved.Substring (next + 1, endPos - next - 1);
2594 if(entityName [0] == '#') {
2597 if(entityName [1] == 'x') {
2599 c = (char) int.Parse ("0" + entityName.Substring (2),
2600 System.Globalization.NumberStyles.HexNumber);
2603 c = (char) int.Parse (entityName.Substring (1));
2605 resolved.Append (c);
2607 switch(entityName) {
2608 case "lt": resolved.Append ("<"); break;
2609 case "gt": resolved.Append (">"); break;
2610 case "amp": resolved.Append ("&"); break;
2611 case "quot": resolved.Append ("\""); break;
2612 case "apos": resolved.Append ("'"); break;
2613 // With respect to "Value", MS document is helpless
2614 // and the implemention returns inconsistent value
2615 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
2616 default: resolved.Append ("&" + entityName + ";"); break;
2620 if(pos > unresolved.Length)
2622 next = unresolved.IndexOf('&', pos);
2624 resolved.Append (unresolved.Substring(pos));
2626 return resolved.ToString();