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 (); }
303 public override void Close ()
305 readState = ReadState.Closed;
306 foreach (XmlParserInput input in parserInputStack.ToArray ())
308 this.currentInput.Close ();
311 public override string GetAttribute (int i)
313 if (i > attributes.Count)
314 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
316 return UnescapeAttributeValue (attributes [orderedAttributes [i]] as string);
319 public override string GetAttribute (string name)
321 return attributes.ContainsKey (name) ?
322 UnescapeAttributeValue (attributes [name] as string) : String.Empty;
325 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
327 for(int i = 0; i < orderedAttributes.Count; i++)
329 string thisName = (string) orderedAttributes [i];
331 int indexOfColon = thisName.IndexOf (':');
333 if (indexOfColon != -1) {
334 string thisLocalName = thisName.Substring (indexOfColon + 1);
336 if (localName == thisLocalName) {
337 string thisPrefix = thisName.Substring (0, indexOfColon);
338 string thisNamespaceURI = LookupNamespace (thisPrefix);
340 if (namespaceURI == thisNamespaceURI)
343 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
349 public override string GetAttribute (string localName, string namespaceURI)
351 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
354 return UnescapeAttributeValue (attributes [orderedAttributes [idx]] as string);
358 public TextReader GetRemainder ()
360 throw new NotImplementedException ();
363 bool IXmlLineInfo.HasLineInfo ()
368 public override string LookupNamespace (string prefix)
370 return parserContext.NamespaceManager.LookupNamespace (prefix);
373 public override void MoveToAttribute (int i)
377 if (attributes == null || orderedAttributes.Count < i || i < 0)
378 throw new ArgumentOutOfRangeException ("attribute index out of range.");
380 string name = orderedAttributes [i] as string;
381 string value = attributes [name] as string;
383 XmlNodeType.Attribute, // nodeType
385 false, // isEmptyElement
387 false // clearAttributes
389 attributeValuePos = 0;
392 public override bool MoveToAttribute (string name)
397 if (attributes == null)
400 if (orderedAttributesEnumerator == null) {
402 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
405 while (orderedAttributesEnumerator.MoveNext ()) {
406 if(name == orderedAttributesEnumerator.Current as string) {
413 string value = attributes [name] as string;
415 XmlNodeType.Attribute, // nodeType
417 false, // isEmptyElement
419 false // clearAttributes
421 attributeValuePos = 0;
427 public override bool MoveToAttribute (string localName, string namespaceName)
431 if (attributes == null)
434 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
437 MoveToAttribute (idx);
441 public override bool MoveToElement ()
443 if (orderedAttributesEnumerator != null) {
444 orderedAttributesEnumerator = null;
446 RestoreProperties ();
453 public override bool MoveToFirstAttribute ()
456 return MoveToNextAttribute ();
459 public override bool MoveToNextAttribute ()
461 if (attributes == null)
464 if (orderedAttributesEnumerator == null) {
466 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
469 if (orderedAttributesEnumerator.MoveNext ()) {
470 string name = orderedAttributesEnumerator.Current as string;
471 string value = attributes [name] as string;
473 XmlNodeType.Attribute, // nodeType
475 false, // isEmptyElement
477 false // clearAttributes
479 attributeValuePos = 0;
486 public override bool Read ()
489 isPropertySaved = false;
490 readState = ReadState.Interactive;
492 // It was moved from end of ReadStartTag ().
497 more = ReadContent ();
502 public override bool ReadAttributeValue ()
504 // 'attributeString' holds real string value (without their
505 // quotation characters).
507 // 'attributeValuePos' holds current position
508 // of 'attributeString' while iterating ReadAttribute().
510 // -1 if ReadAttributeValue() has already finished.
511 // 0 if ReadAttributeValue() ready to start reading.
512 // >0 if ReadAttributeValue() already got 1 or more values
514 // local 'refPosition' holds the position on the
515 // attributeString which may be used next time.
517 if (attributeValuePos < 0) {
518 SetProperties (XmlNodeType.None,
526 // If not started, then initialize attributeString when parsing is at start.
527 if (attributeValuePos == 0)
529 value.Substring (1, value.Length - 2);
531 returnEntityReference = false;
532 value = String.Empty;
537 refPosition = attributeString.IndexOf ('&', attributeValuePos);
538 if (refPosition < 0) {
539 // Reached to the end of value string.
540 value += attributeString.Substring (attributeValuePos);
541 attributeValuePos = -1;
543 } else if (refPosition == attributeValuePos) {
544 string parsed = ReadAttributeValueReference ();
548 // Found that an entity reference starts from this point.
549 // reset position to after '&'.
550 attributeValuePos = refPosition;
551 if (value.Length <= 0) {
552 int endNamePos = attributeString.IndexOf (";", attributeValuePos);
553 value = attributeString.Substring (attributeValuePos+1, endNamePos - attributeValuePos - 1);
554 attributeValuePos += value.Length + 2;
555 returnEntityReference = true;
560 value += attributeString.Substring (attributeValuePos,
561 refPosition - attributeValuePos);
562 attributeValuePos = refPosition;
565 } while (++loop > 0);
567 if (returnEntityReference)
568 SetProperties (XmlNodeType.EntityReference,
574 SetProperties (XmlNodeType.Text,
584 public int ReadBase64 (byte [] buffer, int offset, int length)
586 throw new NotImplementedException ();
590 public int ReadBinHex (byte [] buffer, int offset, int length)
592 throw new NotImplementedException ();
596 public int ReadChars (char [] buffer, int offset, int length)
598 throw new NotImplementedException ();
601 public override string ReadInnerXml ()
603 if (readState != ReadState.Interactive)
607 case XmlNodeType.Attribute:
608 return value.Substring (1, value.Length - 2);
609 case XmlNodeType.Element:
613 int startDepth = depth;
615 if (innerXmlBuilder == null)
616 innerXmlBuilder = new StringBuilder ();
617 innerXmlBuilder.Length = 0;
621 if (NodeType ==XmlNodeType.None)
622 throw new XmlException ("unexpected end of xml.");
623 else if (NodeType == XmlNodeType.EndElement && depth == startDepth)
626 innerXmlBuilder.Append (currentTag);
628 string xml = innerXmlBuilder.ToString ();
629 innerXmlBuilder.Length = 0;
631 case XmlNodeType.None:
632 // MS document is incorrect. Seems not to progress.
640 public override string ReadOuterXml ()
642 if (readState != ReadState.Interactive)
646 case XmlNodeType.Attribute:
647 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
648 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
649 case XmlNodeType.Element:
650 bool isEmpty = IsEmptyElement;
651 string startTag = currentTag.ToString ();
654 if (NodeType == XmlNodeType.Element && !isEmpty)
655 return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
657 return currentTag.ToString ();
658 case XmlNodeType.None:
659 // MS document is incorrect. Seems not to progress.
667 public override string ReadString ()
669 if (readStringBuffer == null)
670 readStringBuffer = new StringBuilder ();
671 readStringBuffer.Length = 0;
676 case XmlNodeType.Element:
682 case XmlNodeType.Text:
683 case XmlNodeType.CDATA:
684 case XmlNodeType.Whitespace:
685 case XmlNodeType.SignificantWhitespace:
686 readStringBuffer.Append (Value);
692 case XmlNodeType.Text:
693 case XmlNodeType.CDATA:
694 case XmlNodeType.Whitespace:
695 case XmlNodeType.SignificantWhitespace:
698 case XmlNodeType.Text:
699 case XmlNodeType.CDATA:
700 case XmlNodeType.Whitespace:
701 case XmlNodeType.SignificantWhitespace:
702 readStringBuffer.Append (Value);
710 string ret = readStringBuffer.ToString ();
711 readStringBuffer.Length = 0;
716 public void ResetState ()
718 throw new NotImplementedException ();
721 public override void ResolveEntity ()
723 // XmlTextReaders don't resolve entities.
724 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
730 // Parsed DTD Objects
731 internal DTDObjectModel currentSubset;
733 internal void Initialize (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
735 parserContext = context;
736 if (context == null) {
737 XmlNameTable nt = new NameTable ();
738 parserContext = new XmlParserContext (nt,
739 new XmlNamespaceManager (nt),
743 if (url != null && url != String.Empty)
744 parserContext.BaseURI = url;
748 case XmlNodeType.Attribute:
751 case XmlNodeType.Element:
752 allowMultipleRoot = true;
754 case XmlNodeType.Document:
757 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
760 this.currentInput = new XmlParserInput (fragment, url);
761 StreamReader sr = fragment as StreamReader;
767 private XmlParserContext parserContext;
769 private XmlParserInput currentInput;
770 private Stack parserInputStack = new Stack ();
771 private ReadState readState;
774 private int elementDepth;
775 private bool depthUp;
777 private bool popScope;
778 private Stack elementStack;
779 private Stack baseURIStack;
780 private bool haveEnteredDocument;
781 private bool allowMultipleRoot = false;
783 private XmlNodeType nodeType;
785 private string prefix;
786 private string localName;
787 private string namespaceURI;
788 private bool isEmptyElement;
789 private string value;
791 private bool isPropertySaved;
792 private XmlNodeType saveNodeType;
793 private string saveName;
794 private string savePrefix;
795 private string saveLocalName;
796 private string saveNamespaceURI;
797 private bool saveIsEmptyElement;
799 private Hashtable attributes;
800 private ArrayList orderedAttributes;
801 private IEnumerator orderedAttributesEnumerator;
803 private bool returnEntityReference;
804 private string entityReferenceName;
806 private char [] nameBuffer;
807 private int nameLength;
808 private int nameCapacity;
809 private const int initialNameCapacity = 256;
811 private char [] valueBuffer;
812 private int valueLength;
813 private int valueCapacity;
814 private const int initialValueCapacity = 8192;
816 // A buffer for ReadContent for ReadOuterXml
817 private StringBuilder currentTag {
819 return currentInput.CurrentMarkup;
823 private string attributeString = String.Empty;
824 private int attributeValuePos;
825 // This should be only referenced(used) by ReadInnerXml(). Kind of flyweight pattern.
826 private StringBuilder innerXmlBuilder;
827 private StringBuilder readStringBuffer;
829 // Parameter entity placeholder
830 private Hashtable parameterEntities = new Hashtable ();
833 private XmlResolver resolver = new XmlUrlResolver ();
835 private bool namespaces = true;
837 private XmlException ReaderError (string message)
839 return new XmlException (message, LineNumber, LinePosition);
844 readState = ReadState.Initial;
850 elementStack = new Stack();
851 baseURIStack = new Stack();
852 haveEnteredDocument = false;
854 nodeType = XmlNodeType.None;
856 prefix = String.Empty;
857 localName = string.Empty;
858 isEmptyElement = false;
859 value = String.Empty;
861 attributes = new Hashtable ();
862 orderedAttributes = new ArrayList ();
863 orderedAttributesEnumerator = null;
865 returnEntityReference = false;
866 entityReferenceName = String.Empty;
868 nameBuffer = new char [initialNameCapacity];
870 nameCapacity = initialNameCapacity;
872 valueBuffer = new char [initialValueCapacity];
874 valueCapacity = initialValueCapacity;
877 // Use this method rather than setting the properties
878 // directly so that all the necessary properties can
879 // be changed in harmony with each other. Maybe the
880 // fields should be in a seperate class to help enforce
882 private void SetProperties (
883 XmlNodeType nodeType,
887 bool clearAttributes)
889 this.nodeType = nodeType;
891 this.isEmptyElement = isEmptyElement;
893 this.elementDepth = depth;
899 int indexOfColon = name.IndexOf (':');
901 if (indexOfColon == -1) {
902 prefix = String.Empty;
905 prefix = name.Substring (0, indexOfColon);
906 localName = name.Substring (indexOfColon + 1);
909 prefix = String.Empty;
913 namespaceURI = LookupNamespace (prefix);
916 private void SaveProperties ()
918 // If already saved, then return.
922 saveNodeType = nodeType;
925 saveLocalName = localName;
926 saveNamespaceURI = namespaceURI;
927 saveIsEmptyElement = isEmptyElement;
928 // An element's value is always String.Empty.
929 isPropertySaved = true;
932 private void RestoreProperties ()
934 nodeType = saveNodeType;
937 localName = saveLocalName;
938 namespaceURI = saveNamespaceURI;
939 isEmptyElement = saveIsEmptyElement;
940 value = String.Empty;
941 isPropertySaved = false;
944 private void AddAttribute (string name, string value)
946 attributes.Add (name, value);
947 orderedAttributes.Add (name);
950 private void ClearAttributes ()
952 if (attributes.Count > 0) {
954 orderedAttributes.Clear ();
957 orderedAttributesEnumerator = null;
960 private int PeekChar ()
962 return currentInput.PeekChar ();
965 private int ReadChar ()
967 return currentInput.ReadChar ();
970 // This should really keep track of some state so
971 // that it's not possible to have more than one document
972 // element or text outside of the document element.
973 private bool ReadContent ()
975 currentTag.Length = 0;
977 parserContext.NamespaceManager.PopScope ();
981 if (returnEntityReference) {
982 SetEntityReferenceProperties ();
984 switch (PeekChar ()) {
989 case '\r': goto case ' ';
990 case '\n': goto case ' ';
991 case '\t': goto case ' ';
993 if (whitespaceHandling == WhitespaceHandling.All ||
994 whitespaceHandling == WhitespaceHandling.Significant)
995 return ReadWhitespace ();
998 return ReadContent ();
1001 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1002 readState = ReadState.EndOfFile;
1004 XmlNodeType.None, // nodeType
1005 String.Empty, // name
1006 false, // isEmptyElement
1007 String.Empty, // value
1008 true // clearAttributes
1016 return this.ReadState != ReadState.EndOfFile;
1019 private void SetEntityReferenceProperties ()
1022 XmlNodeType.EntityReference, // nodeType
1023 entityReferenceName, // name
1024 false, // isEmptyElement
1025 String.Empty, // value
1026 true // clearAttributes
1029 returnEntityReference = false;
1030 entityReferenceName = String.Empty;
1033 // The leading '<' has already been consumed.
1034 private void ReadTag ()
1036 switch (PeekChar ())
1044 ReadProcessingInstruction ();
1056 // The leading '<' has already been consumed.
1057 private void ReadStartTag ()
1059 parserContext.NamespaceManager.PushScope ();
1061 string name = ReadName ();
1062 if (haveEnteredDocument && elementStack.Count == 0 && !allowMultipleRoot)
1063 throw ReaderError("document has terminated, cannot open new element");
1065 haveEnteredDocument = true;
1068 bool isEmptyElement = false;
1072 if (XmlConstructs.IsNameStart (PeekChar ()))
1073 ReadAttributes (false);
1075 if (PeekChar () == '/') {
1077 isEmptyElement = true;
1082 elementStack.Push (name);
1083 baseURIStack.Push (attributes ["xml:base"] != null ?
1084 attributes ["xml:base"] : BaseURI);
1090 XmlNodeType.Element, // nodeType
1092 isEmptyElement, // isEmptyElement
1093 String.Empty, // value
1094 false // clearAttributes
1098 // The reader is positioned on the first character
1099 // of the element's name.
1100 private void ReadEndTag ()
1102 string name = ReadName ();
1103 if (elementStack.Count == 0)
1104 throw ReaderError("closing element without matching opening element");
1105 string expected = (string)elementStack.Pop();
1106 if (expected != name)
1107 throw ReaderError(String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1108 baseURIStack.Pop ();
1116 XmlNodeType.EndElement, // nodeType
1118 false, // isEmptyElement
1119 String.Empty, // value
1120 true // clearAttributes
1126 private void AppendNameChar (int ch)
1128 CheckNameCapacity ();
1129 nameBuffer [nameLength++] = (char)ch;
1132 private void CheckNameCapacity ()
1134 if (nameLength == nameCapacity) {
1135 nameCapacity = nameCapacity * 2;
1136 char [] oldNameBuffer = nameBuffer;
1137 nameBuffer = new char [nameCapacity];
1138 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1142 private string CreateNameString ()
1144 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1147 private void AppendValueChar (int ch)
1149 CheckValueCapacity ();
1150 valueBuffer [valueLength++] = (char)ch;
1153 private void CheckValueCapacity ()
1155 if (valueLength == valueCapacity) {
1156 valueCapacity = valueCapacity * 2;
1157 char [] oldValueBuffer = valueBuffer;
1158 valueBuffer = new char [valueCapacity];
1159 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1163 private string CreateValueString ()
1165 return new String (valueBuffer, 0, valueLength);
1168 // The reader is positioned on the first character
1170 private void ReadText (bool cleanValue)
1175 int ch = PeekChar ();
1177 while (ch != '<' && ch != -1) {
1180 if (ReadReference (false))
1183 AppendValueChar (ReadChar ());
1188 if (returnEntityReference && valueLength == 0) {
1189 SetEntityReferenceProperties ();
1192 XmlNodeType.Text, // nodeType
1193 String.Empty, // name
1194 false, // isEmptyElement
1195 CreateValueString (), // value
1196 true // clearAttributes
1201 // The leading '&' has already been consumed.
1202 // Returns true if the entity reference isn't a simple
1203 // character reference or one of the predefined entities.
1204 // This allows the ReadText method to break so that the
1205 // next call to Read will return the EntityReference node.
1206 private bool ReadReference (bool ignoreEntityReferences)
1208 if (PeekChar () == '#') {
1210 ReadCharacterReference ();
1212 ReadEntityReference (ignoreEntityReferences);
1214 return returnEntityReference;
1217 private void ReadCharacterReference ()
1221 if (PeekChar () == 'x') {
1224 while (PeekChar () != ';' && PeekChar () != -1) {
1225 int ch = ReadChar ();
1227 if (ch >= '0' && ch <= '9')
1228 value = (value << 4) + ch - '0';
1229 else if (ch >= 'A' && ch <= 'F')
1230 value = (value << 4) + ch - 'A' + 10;
1231 else if (ch >= 'a' && ch <= 'f')
1232 value = (value << 4) + ch - 'a' + 10;
1236 "invalid hexadecimal digit: {0} (#x{1:X})",
1241 while (PeekChar () != ';' && PeekChar () != -1) {
1242 int ch = ReadChar ();
1244 if (ch >= '0' && ch <= '9')
1245 value = value * 10 + ch - '0';
1249 "invalid decimal digit: {0} (#x{1:X})",
1257 AppendValueChar (value);
1260 private void ReadEntityReference (bool ignoreEntityReferences)
1264 int ch = PeekChar ();
1266 while (ch != ';' && ch != -1) {
1267 AppendNameChar (ReadChar ());
1273 string name = CreateNameString ();
1278 AppendValueChar ('<');
1281 AppendValueChar ('>');
1284 AppendValueChar ('&');
1287 AppendValueChar ('\'');
1290 AppendValueChar ('"');
1293 if (ignoreEntityReferences) {
1294 AppendValueChar ('&');
1296 foreach (char ch2 in name) {
1297 AppendValueChar (ch2);
1300 AppendValueChar (';');
1302 returnEntityReference = true;
1303 entityReferenceName = name;
1309 // The reader is positioned on the first character of
1310 // the attribute name.
1311 private void ReadAttributes (bool allowPIEnd)
1315 string name = ReadName ();
1319 string value = ReadAttribute ();
1322 if (name == "xmlns")
1323 parserContext.NamespaceManager.AddNamespace (String.Empty, UnescapeAttributeValue (value));
1324 else if (name.StartsWith ("xmlns:"))
1325 parserContext.NamespaceManager.AddNamespace (name.Substring (6), UnescapeAttributeValue (value));
1327 AddAttribute (name, value);
1328 peekChar = PeekChar ();
1329 if (peekChar == '?' && allowPIEnd)
1331 } while (peekChar != '/' && peekChar != '>' && peekChar != -1);
1334 // The reader is positioned on the quote character.
1335 // *Keeps quote char* to value to get_QuoteChar() correctly.
1336 private string ReadAttribute ()
1340 int quoteChar = ReadChar ();
1342 if (quoteChar != '\'' && quoteChar != '\"')
1343 throw ReaderError ("an attribute value was not quoted");
1345 AppendValueChar (quoteChar);
1347 while (PeekChar () != quoteChar) {
1348 int ch = ReadChar ();
1353 throw ReaderError ("attribute values cannot contain '<'");
1355 throw ReaderError ("unexpected end of file in an attribute value");
1357 AppendValueChar (ch);
1362 ReadChar (); // quoteChar
1363 AppendValueChar (quoteChar);
1365 return CreateValueString ();
1368 // The reader is positioned on the first character
1371 // It may be xml declaration or processing instruction.
1372 private void ReadProcessingInstruction ()
1374 string target = ReadName ();
1376 if (target == "xml") {
1377 ReadXmlDeclaration ();
1383 while (PeekChar () != -1) {
1384 int ch = ReadChar ();
1386 if (ch == '?' && PeekChar () == '>') {
1391 AppendValueChar ((char)ch);
1395 XmlNodeType.ProcessingInstruction, // nodeType
1397 false, // isEmptyElement
1398 CreateValueString (), // value
1399 true // clearAttributes
1403 // The reader is positioned after "<?xml "
1404 private void ReadXmlDeclaration ()
1408 if (XmlConstructs.IsNameStart (PeekChar ()))
1409 ReadAttributes (true);
1413 XmlNodeType.XmlDeclaration, // nodeType
1415 false, // isEmptyElement
1416 currentInput.CurrentMarkup.ToString (6, currentInput.CurrentMarkup.Length - 6), // value
1417 false // clearAttributes
1421 // The reader is positioned on the first character after
1422 // the leading '<!'.
1423 private void ReadDeclaration ()
1425 int ch = PeekChar ();
1445 // The reader is positioned on the first character after
1446 // the leading '<!--'.
1447 private void ReadComment ()
1451 while (PeekChar () != -1) {
1452 int ch = ReadChar ();
1454 if (ch == '-' && PeekChar () == '-') {
1457 if (PeekChar () != '>')
1458 throw ReaderError ("comments cannot contain '--'");
1464 AppendValueChar ((char)ch);
1468 XmlNodeType.Comment, // nodeType
1469 String.Empty, // name
1470 false, // isEmptyElement
1471 CreateValueString (), // value
1472 true // clearAttributes
1476 // The reader is positioned on the first character after
1477 // the leading '<![CDATA['.
1478 private void ReadCDATA ()
1484 while (PeekChar () != -1) {
1489 if (ch == ']' && PeekChar () == ']') {
1490 ch = ReadChar (); // ']'
1492 if (PeekChar () == '>') {
1497 // AppendValueChar (']');
1498 // AppendValueChar (']');
1499 // ch = ReadChar ();
1503 AppendValueChar ((char)ch);
1507 XmlNodeType.CDATA, // nodeType
1508 String.Empty, // name
1509 false, // isEmptyElement
1510 CreateValueString (), // value
1511 true // clearAttributes
1515 // The reader is positioned on the first character after
1516 // the leading '<!DOCTYPE'.
1517 private void ReadDoctypeDecl ()
1519 string doctypeName = null;
1520 string publicId = String.Empty;
1521 string systemId = String.Empty;
1522 int intSubsetStartLine = 0;
1523 int intSubsetStartColumn = 0;
1526 doctypeName = ReadName ();
1531 systemId = ReadSystemLiteral (true);
1534 publicId = ReadPubidLiteral ();
1536 systemId = ReadSystemLiteral (false);
1542 if(PeekChar () == '[')
1544 // read markupdecl etc. or end of decl
1546 intSubsetStartLine = this.LineNumber;
1547 intSubsetStartColumn = this.LinePosition;
1548 int startPos = currentTag.Length;
1550 ReadInternalSubset ();
1551 // } while (nodeType != XmlNodeType.None);
1552 int endPos = currentTag.Length - 1;
1553 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1555 // end of DOCTYPE decl.
1560 currentSubset = new DTDObjectModel (); // merges both internal and external subsets in the meantime,
1561 int originalParserDepth = parserInputStack.Count;
1562 if (intSubsetStartLine > 0) {
1563 XmlParserInput original = currentInput;
1564 currentInput = new XmlParserInput (new StringReader (parserContext.InternalSubset), BaseURI, intSubsetStartLine, intSubsetStartColumn);
1566 CompileDTDSubset ();
1567 if (PeekChar () == -1 && parserInputStack.Count > 0)
1569 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth);
1570 if (dtdIncludeSect != 0)
1571 this.ReaderError ("INCLUDE section is not ended correctly.");
1572 currentInput = original;
1574 if (systemId != String.Empty) {
1575 pushParserInput (systemId);
1577 this.CompileDTDSubset ();
1578 if (PeekChar () == -1 && parserInputStack.Count > 1)
1580 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth + 1);
1584 // set properties for <!DOCTYPE> node
1586 XmlNodeType.DocumentType, // nodeType
1587 doctypeName, // name
1588 false, // isEmptyElement
1589 parserContext.InternalSubset, // value
1590 true // clearAttributes
1594 private void pushParserInput (string url)
1596 string absPath = null;
1599 Uri baseUrl = new Uri (BaseURI);
1600 absPath = resolver.ResolveUri (baseUrl, url).ToString ();
1601 } catch (UriFormatException) {
1602 if (Path.IsPathRooted (url))
1604 else if (BaseURI != String.Empty)
1605 absPath = new FileInfo (BaseURI).DirectoryName + Path.DirectorySeparatorChar + url;
1610 if (Path.IsPathRooted (url))
1612 else if (BaseURI != String.Empty)
1613 absPath = new FileInfo (BaseURI).DirectoryName + Path.DirectorySeparatorChar + url;
1617 foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1618 if (i.BaseURI == url)
1619 this.ReaderError ("Nested inclusion is not allowed: " + url);
1621 parserInputStack.Push (currentInput);
1622 currentInput = new XmlParserInput (new XmlStreamReader (absPath, false), absPath);
1623 baseURIStack.Push (BaseURI);
1624 parserContext.BaseURI = absPath;
1627 private void popParserInput ()
1629 currentInput = parserInputStack.Pop () as XmlParserInput;
1630 parserContext.BaseURI = this.baseURIStack.Pop () as string;
1633 private enum DtdInputState
\r
1642 InsideSingleQuoted,
\r
1643 InsideDoubleQuoted,
\r
1646 private class DtdInputStateStack
\r
1648 Stack intern = new Stack ();
\r
1649 public DtdInputStateStack ()
\r
1651 Push (DtdInputState.Free);
\r
1654 public DtdInputState Peek ()
\r
1656 return (DtdInputState) intern.Peek ();
\r
1659 public DtdInputState Pop ()
\r
1661 return (DtdInputState) intern.Pop ();
\r
1664 public void Push (DtdInputState val)
\r
1666 intern.Push (val);
\r
1671 DtdInputStateStack stateStack = new DtdInputStateStack ();
\r
1672 DtdInputState State {
\r
1673 get { return stateStack.Peek (); }
\r
1676 // Simply read but not generate any result.
1677 private void ReadInternalSubset ()
\r
1679 bool continueParse = true;
\r
1681 while (continueParse) {
\r
1682 switch (ReadChar ()) {
\r
1685 case DtdInputState.Free:
\r
1686 continueParse = false;
\r
1688 case DtdInputState.InsideDoubleQuoted:
\r
1690 case DtdInputState.InsideSingleQuoted:
\r
1693 throw ReaderError ("unexpected end of file at DTD.");
\r
1697 throw ReaderError ("unexpected end of file at DTD.");
\r
1699 if (State == DtdInputState.InsideDoubleQuoted ||
\r
1700 State == DtdInputState.InsideSingleQuoted)
\r
1701 continue; // well-formed
\r
1702 switch (ReadChar ()) {
\r
1704 stateStack.Push (DtdInputState.PI);
\r
1707 switch (ReadChar ()) {
\r
1709 switch (ReadChar ()) {
\r
1712 stateStack.Push (DtdInputState.ElementDecl);
\r
1716 stateStack.Push (DtdInputState.EntityDecl);
\r
1719 throw ReaderError ("unexpected token '<!E'.");
\r
1723 Expect ("TTLIST");
\r
1724 stateStack.Push (DtdInputState.AttlistDecl);
\r
1727 Expect ("OTATION");
\r
1728 stateStack.Push (DtdInputState.NotationDecl);
\r
1732 stateStack.Push (DtdInputState.Comment);
\r
1737 throw ReaderError ("unexpected '>'.");
\r
1741 if (State == DtdInputState.InsideSingleQuoted)
\r
1742 stateStack.Pop ();
\r
1743 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
\r
1744 stateStack.Push (DtdInputState.InsideSingleQuoted);
\r
1747 if (State == DtdInputState.InsideDoubleQuoted)
\r
1748 stateStack.Pop ();
\r
1749 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
\r
1750 stateStack.Push (DtdInputState.InsideDoubleQuoted);
\r
1754 case DtdInputState.ElementDecl:
\r
1755 goto case DtdInputState.NotationDecl;
\r
1756 case DtdInputState.AttlistDecl:
\r
1757 goto case DtdInputState.NotationDecl;
\r
1758 case DtdInputState.EntityDecl:
\r
1759 goto case DtdInputState.NotationDecl;
\r
1760 case DtdInputState.NotationDecl:
\r
1761 stateStack.Pop ();
\r
1763 case DtdInputState.InsideDoubleQuoted:
\r
1765 case DtdInputState.InsideSingleQuoted:
\r
1766 continue; // well-formed
\r
1767 case DtdInputState.Comment:
\r
1770 throw ReaderError ("unexpected token '>'");
\r
1774 if (State == DtdInputState.PI) {
\r
1775 if (ReadChar () == '>')
\r
1776 stateStack.Pop ();
\r
1780 if (State == DtdInputState.Comment) {
\r
1781 if (PeekChar () == '-') {
\r
1784 stateStack.Pop ();
\r
1789 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
\r
1790 throw ReaderError ("Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
\r
1796 // Read any one of following:
1797 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1798 // PI, Comment, Parameter Entity, or doctype termination char(']')
1800 // returns a node of some nodeType or null, setting nodeType.
1801 // (if None then ']' was found.)
1802 private void CompileDTDSubset()
1808 nodeType = XmlNodeType.None;
1818 // Only read, no store.
1819 ReadProcessingInstruction ();
1822 CompileDeclaration ();
1825 throw ReaderError ("Syntax Error after '<' character.");
1835 throw ReaderError (String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
1839 private void CompileDeclaration ()
1841 nodeType = XmlNodeType.DocumentType; // Hack!!
1846 // Only read, no store.
1856 if (PeekChar () == '%') {
1858 if (!XmlConstructs.IsSpace (PeekChar ())) {
1861 // throw ReaderError ("expected whitespace between '%' and name.");
1865 if (XmlConstructs.IsName (PeekChar ()))
1866 ReadParameterEntityDecl ();
1868 throw ReaderError ("expected name character");
1872 DTDEntityDeclaration ent = ReadEntityDecl ();
1873 if (currentSubset.EntityDecls [ent.Name] == null)
1874 currentSubset.EntityDecls.Add (ent.Name, ent);
1878 DTDElementDeclaration el = ReadElementDecl ();
1879 currentSubset.ElementDecls.Add (el.Name, el);
1882 throw ReaderError ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1887 DTDAttListDeclaration atl = ReadAttListDecl ();
1888 if (currentSubset.AttListDecls.ContainsKey (atl.Name))
1889 currentSubset.AttListDecls.Add (atl.Name, atl);
1893 DTDNotationDeclaration not = ReadNotationDecl ();
1894 currentSubset.NotationDecls.Add (not.Name, not);
1897 // conditional sections
1902 switch (ReadChar ()) {
1916 throw ReaderError ("Syntax Error after '<!' characters.");
1920 private void ReadIgnoreSect ()
1925 int dtdIgnoreSect = 1;
1926 while (dtdIgnoreSect > 0) {
1927 switch (skip ? PeekChar () : ReadChar ()) {
1929 throw ReaderError ("Unexpected IGNORE section end.");
1932 if (ReadChar () == '!' && ReadChar () == '[')
1936 if (ReadChar () == ']') {
1937 if (ReadChar () == '>')
1948 // The reader is positioned on the head of the name.
\r
1949 private DTDElementDeclaration ReadElementDecl ()
\r
1951 DTDElementDeclaration decl = new DTDElementDeclaration ();
\r
1952 SkipWhitespace ();
\r
1953 TryExpandPERef ();
\r
1954 decl.Name = ReadName ();
\r
1955 SkipWhitespace ();
\r
1956 TryExpandPERef ();
\r
1957 ReadContentSpec (decl);
\r
1958 SkipWhitespace ();
\r
1963 // read 'children'(BNF) of contentspec
\r
1964 private void ReadContentSpec (DTDElementDeclaration decl)
\r
1966 switch(PeekChar ())
\r
1969 decl.IsEmpty = true;
\r
1973 decl.IsAny = true;
\r
1977 DTDContentModel model = decl.ContentModel;
\r
1979 SkipWhitespace ();
\r
1980 TryExpandPERef ();
\r
1981 if(PeekChar () == '#') {
\r
1983 decl.IsMixedContent = true;
\r
1984 Expect ("#PCDATA");
\r
1985 SkipWhitespace ();
\r
1986 TryExpandPERef ();
\r
1987 SkipWhitespace ();
\r
1988 while(PeekChar () != ')') {
\r
1990 SkipWhitespace ();
\r
1991 TryExpandPERef ();
\r
1992 SkipWhitespace ();
\r
1993 model.ChildModels.Add (ReadName ());
\r
1994 SkipWhitespace ();
\r
1995 TryExpandPERef ();
\r
1998 if(PeekChar () == '*')
\r
1999 ReadChar (); // ZeroOrMore
\r
2001 // Non-Mixed Contents
\r
2002 model.ChildModels.Add (ReadCP ());
\r
2003 SkipWhitespace ();
\r
2005 do { // copied from ReadCP() ...;-)
\r
2006 TryExpandPERef ();
\r
2007 SkipWhitespace ();
\r
2008 if(PeekChar ()=='|') {
\r
2010 model.OrderType = DTDContentOrderType.Or;
\r
2012 SkipWhitespace ();
\r
2013 model.ChildModels.Add (ReadCP ());
\r
2014 SkipWhitespace ();
\r
2016 else if(PeekChar () == ',')
\r
2019 model.OrderType = DTDContentOrderType.Seq;
\r
2021 SkipWhitespace ();
\r
2022 model.ChildModels.Add (ReadCP ());
\r
2023 SkipWhitespace ();
\r
2031 switch(PeekChar ())
\r
2034 model.MinOccurs = 0;
\r
2038 model.MinOccurs = 0;
\r
2039 model.MaxOccurs = decimal.MaxValue;
\r
2043 model.MaxOccurs = decimal.MaxValue;
\r
2047 SkipWhitespace ();
\r
2049 SkipWhitespace ();
\r
2054 // Read 'cp' (BNF) of contentdecl (BNF)
\r
2055 private DTDContentModel ReadCP ()
\r
2057 DTDContentModel model = new DTDContentModel ();
\r
2058 TryExpandPERef ();
\r
2059 if(PeekChar () == '(') {
\r
2061 SkipWhitespace ();
\r
2062 model.ChildModels.Add (ReadCP ());
\r
2063 SkipWhitespace ();
\r
2065 TryExpandPERef ();
\r
2066 SkipWhitespace ();
\r
2067 if(PeekChar ()=='|') {
\r
2069 model.OrderType = DTDContentOrderType.Or;
\r
2071 SkipWhitespace ();
\r
2072 model.ChildModels.Add (ReadCP ());
\r
2073 SkipWhitespace ();
\r
2075 else if(PeekChar () == ',') {
\r
2077 model.OrderType = DTDContentOrderType.Seq;
\r
2079 SkipWhitespace ();
\r
2080 model.ChildModels.Add (ReadCP ());
\r
2081 SkipWhitespace ();
\r
2087 SkipWhitespace ();
\r
2091 TryExpandPERef ();
\r
2092 model.ElementName = ReadName ();
\r
2095 switch(PeekChar ()) {
\r
2097 model.MinOccurs = 0;
\r
2101 model.MinOccurs = 0;
\r
2102 model.MaxOccurs = decimal.MaxValue;
\r
2106 model.MaxOccurs = decimal.MaxValue;
\r
2113 // The reader is positioned on the first name char.
\r
2114 private void ReadParameterEntityDecl ()
\r
2116 DTDParameterEntityDeclaration decl =
\r
2117 new DTDParameterEntityDeclaration();
\r
2118 decl.BaseURI = BaseURI;
\r
2120 decl.Name = ReadName ();
\r
2121 SkipWhitespace ();
\r
2123 if (PeekChar () == 'S' || PeekChar () == 'P') {
\r
2124 // throw new NotImplementedException ("External parameter entity reference is not implemented yet.");
\r
2125 // read publicId/systemId
\r
2126 ReadExternalID ();
\r
2127 decl.PublicId = attributes ["PUBLIC"] as string;
\r
2128 decl.SystemId = attributes ["SYSTEM"] as string;
\r
2129 SkipWhitespace ();
\r
2132 TryExpandPERef ();
\r
2133 int quoteChar = ReadChar ();
\r
2134 int start = currentTag.Length;
\r
2136 SkipWhitespace ();
\r
2137 int c = PeekChar ();
\r
2138 if ((int) c == -1)
\r
2139 throw new XmlException ("unexpected end of stream in entity value definition.");
\r
2143 if (quoteChar == '"') goto SKIP;
\r
2147 if (quoteChar == '\'') goto SKIP;
\r
2158 decl.Value = currentTag.ToString (start, currentTag.Length - start - 1);
\r
2160 SkipWhitespace ();
\r
2162 if (parameterEntities [decl.Name] == null) {
\r
2163 parameterEntities.Add (decl.Name, decl);
\r
2167 // reader is positioned on '%'
\r
2168 private void ImportAsPERef ()
\r
2170 StringBuilder sb = null;
\r
2171 int peRefStart = currentTag.Length;
\r
2172 string appendStr = "";
\r
2174 string peName = ReadName ();
\r
2176 DTDParameterEntityDeclaration peDecl =
\r
2177 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
\r
2178 if (peDecl == null)
\r
2179 throw ReaderError ("Parameter entity " + peName + " not found.");
\r
2180 if (peDecl.SystemId != null) {
\r
2181 pushParserInput (peDecl.SystemId);
\r
2183 sb = new StringBuilder ();
\r
2186 while (PeekChar () != -1)
\r
2187 sb.Append (ReadChar ());
\r
2188 popParserInput ();
\r
2189 appendStr = sb.ToString ();
\r
2191 appendStr = peDecl.Value;
\r
2193 currentTag.Remove (peRefStart,
\r
2194 currentTag.Length - peRefStart);
\r
2195 currentTag.Append (Dereference (appendStr));
\r
2198 // The reader is positioned on the head of the name.
\r
2199 private DTDEntityDeclaration ReadEntityDecl ()
\r
2201 DTDEntityDeclaration decl = new DTDEntityDeclaration ();
\r
2202 decl.Name = ReadName ();
\r
2203 SkipWhitespace ();
\r
2204 TryExpandPERef ();
\r
2205 SkipWhitespace ();
\r
2207 if (PeekChar () == 'S' || PeekChar () == 'P') {
\r
2208 // external entity
\r
2209 ReadExternalID ();
\r
2210 decl.PublicId = attributes ["PUBLIC"] as string;
\r
2211 decl.SystemId = attributes ["SYSTEM"] as string;
\r
2212 SkipWhitespace ();
\r
2213 if (PeekChar () == 'N')
\r
2217 SkipWhitespace ();
\r
2218 decl.NotationName = ReadName (); // ndata_name
\r
2223 decl.EntityValue = ReadEntityValueDecl ();
\r
2225 SkipWhitespace ();
\r
2230 private string ReadEntityValueDecl ()
\r
2232 SkipWhitespace ();
\r
2233 // quotation char will be finally removed on unescaping
\r
2234 int quoteChar = ReadChar ();
\r
2235 int start = currentTag.Length;
\r
2236 if (quoteChar != '\'' && quoteChar != '"')
\r
2237 throw new XmlException ("quotation char was expected.");
\r
2239 while (PeekChar () != quoteChar) {
\r
2240 switch (PeekChar ()) {
\r
2242 this.ImportAsPERef ();
\r
2247 // ReadCharacterReference ();
\r
2248 ReadReference (true);
\r
2251 throw new XmlException ("unexpected end of stream.");
\r
2257 string value = Dereference (currentTag.ToString (start, currentTag.Length - start));
\r
2258 Expect (quoteChar);
\r
2262 private DTDAttListDeclaration ReadAttListDecl ()
\r
2264 SkipWhitespace ();
\r
2265 TryExpandPERef ();
\r
2266 string name = ReadName (); // target element name
\r
2267 DTDAttListDeclaration decl =
\r
2268 currentSubset.AttListDecls [name] as DTDAttListDeclaration;
\r
2270 decl = new DTDAttListDeclaration ();
\r
2273 SkipWhitespace ();
\r
2274 TryExpandPERef ();
\r
2275 SkipWhitespace ();
\r
2277 while (XmlConstructs.IsName ((char) PeekChar ())) {
\r
2278 DTDAttributeDefinition def = ReadAttributeDefinition ();
\r
2279 if (decl.AttributeDefinitions [def.Name] == null)
\r
2280 decl.AttributeDefinitions.Add (def.Name, def);
\r
2281 SkipWhitespace ();
\r
2282 TryExpandPERef ();
\r
2283 SkipWhitespace ();
\r
2285 SkipWhitespace ();
\r
2290 private DTDAttributeDefinition ReadAttributeDefinition ()
\r
2292 DTDAttributeDefinition def = new DTDAttributeDefinition ();
\r
2295 TryExpandPERef ();
\r
2296 def.Name = ReadName ();
\r
2297 SkipWhitespace ();
\r
2300 TryExpandPERef ();
\r
2301 switch(PeekChar ()) {
\r
2302 case 'C': // CDATA
\r
2304 def.AttributeType = DTDAttributeType.CData;
\r
2306 case 'I': // ID, IDREF, IDREFS
\r
2308 if(PeekChar () == 'R') {
\r
2310 if(PeekChar () == 'S') {
\r
2313 def.AttributeType = DTDAttributeType.IdRefs;
\r
2316 def.AttributeType = DTDAttributeType.IdRef;
\r
2319 def.AttributeType = DTDAttributeType.Id;
\r
2321 case 'E': // ENTITY, ENTITIES
\r
2323 switch(ReadChar ()) {
\r
2324 case 'Y': // ENTITY
\r
2325 def.AttributeType = DTDAttributeType.Entity;
\r
2327 case 'I': // ENTITIES
\r
2329 def.AttributeType = DTDAttributeType.Entities;
\r
2333 case 'N': // NMTOKEN, NMTOKENS, NOTATION
\r
2335 switch(PeekChar ()) {
\r
2337 Expect ("MTOKEN");
\r
2338 if(PeekChar ()=='S') { // NMTOKENS
\r
2340 def.AttributeType = DTDAttributeType.NmTokens;
\r
2343 def.AttributeType = DTDAttributeType.NmToken;
\r
2346 Expect ("OTATION");
\r
2347 def.AttributeType = DTDAttributeType.Notation;
\r
2348 SkipWhitespace ();
\r
2350 SkipWhitespace ();
\r
2351 def.EnumeratedNotations.Add (ReadName ()); // notation name
\r
2352 SkipWhitespace ();
\r
2353 while(PeekChar () == '|') {
\r
2355 SkipWhitespace ();
\r
2356 def.EnumeratedNotations.Add (ReadName ()); // notation name
\r
2357 SkipWhitespace ();
\r
2362 throw new XmlException ("attribute declaration syntax error.");
\r
2365 default: // Enumerated Values
\r
2366 TryExpandPERef ();
\r
2368 SkipWhitespace ();
\r
2369 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
\r
2370 SkipWhitespace ();
\r
2371 while(PeekChar () == '|') {
\r
2373 SkipWhitespace ();
\r
2374 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
\r
2375 SkipWhitespace ();
\r
2380 SkipWhitespace ();
\r
2382 TryExpandPERef ();
\r
2385 if(PeekChar () == '#')
\r
2388 switch(PeekChar ())
\r
2391 Expect ("REQUIRED");
\r
2392 def.OccurenceType = DTDAttributeOccurenceType.Required;
\r
2395 Expect ("IMPLIED");
\r
2396 def.OccurenceType = DTDAttributeOccurenceType.Optional;
\r
2400 def.OccurenceType = DTDAttributeOccurenceType.Fixed;
\r
2401 SkipWhitespace ();
\r
2402 def.UnresolvedDefaultValue = ReadAttribute ();
\r
2406 // one of the enumerated value
\r
2407 if (PeekChar () == -1) {
\r
2408 popParserInput ();
\r
2410 SkipWhitespace ();
\r
2411 def.UnresolvedDefaultValue = ReadAttribute ();
\r
2417 private DTDNotationDeclaration ReadNotationDecl()
\r
2419 DTDNotationDeclaration decl = new DTDNotationDeclaration ();
\r
2420 SkipWhitespace ();
\r
2421 decl.Name = ReadName (); // notation name
\r
2422 if (namespaces) { // copy from SetProperties ;-)
2423 int indexOfColon = decl.Name.IndexOf (':');
2425 if (indexOfColon == -1) {
2426 decl.Prefix = String.Empty;
2427 decl.LocalName = decl.Name;
2429 decl.Prefix = decl.Name.Substring (0, indexOfColon);
2430 decl.LocalName = decl.Name.Substring (indexOfColon + 1);
2433 decl.Prefix = String.Empty;
2434 decl.LocalName = decl.Name;
2437 SkipWhitespace ();
\r
2438 if(PeekChar () == 'P') {
\r
2439 decl.PublicId = ReadPubidLiteral ();
\r
2440 SkipWhitespace ();
\r
2441 if (PeekChar () == '\'' || PeekChar () == '"') {
\r
2442 decl.SystemId = ReadSystemLiteral (false);
\r
2443 SkipWhitespace ();
\r
2445 } else if(PeekChar () == 'S') {
\r
2446 decl.SystemId = ReadSystemLiteral (true);
\r
2447 SkipWhitespace ();
\r
2449 if(decl.PublicId == null && decl.SystemId == null)
\r
2450 throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
\r
2455 private void TryExpandPERef ()
\r
2457 if (PeekChar () == '%') {
\r
2459 if (!XmlConstructs.IsName (PeekChar ()))
\r
2465 // reader is positioned on the first letter of the name.
\r
2466 private void ExpandPERef ()
\r
2468 ExpandPERef (true);
\r
2471 private void ExpandPERef (bool attachSpace)
\r
2473 string peName = ReadName ();
\r
2475 ExpandNamedPERef (peName, attachSpace);
\r
2478 private void ExpandNamedPERef (string peName, bool attachSpace)
\r
2480 DTDParameterEntityDeclaration decl =
\r
2481 parameterEntities [peName] as DTDParameterEntityDeclaration;
\r
2483 throw new XmlException ("undeclared parameter entity: '" + peName + "'");
\r
2484 if (decl.SystemId != null) {
\r
2485 pushParserInput (decl.SystemId);
\r
2489 currentInput.InsertParameterEntityBuffer (attachSpace ? " " + Dereference (decl.Value) + " " : decl.Value);
\r
2490 SkipWhitespace (); // is it ok?
\r
2491 // while (PeekChar () == '%')
\r
2492 // TryExpandPERef (); // recursive
\r
2495 private void ReadExternalID() {
\r
2496 switch(PeekChar ()) {
\r
2498 attributes ["PUBLIC"] = null;
\r
2499 attributes ["SYSTEM"] = ReadSystemLiteral (true);
\r
2502 attributes ["PUBLIC"] = ReadPubidLiteral ();
\r
2503 SkipWhitespace ();
\r
2504 attributes ["SYSTEM"] = ReadSystemLiteral (false);
\r
2509 // The reader is positioned on the first 'S' of "SYSTEM".
2510 private string ReadSystemLiteral (bool expectSYSTEM)
2515 int quoteChar = ReadChar (); // apos or quot
2516 int startPos = currentTag.Length;
2518 while(c != quoteChar) {
2520 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
2522 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2525 private string ReadPubidLiteral()
2529 int quoteChar = ReadChar ();
2530 int startPos = currentTag.Length;
2532 while(c != quoteChar)
2535 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
2536 if(c != quoteChar && !XmlConstructs.IsPubid (c))
2537 throw ReaderError("character '" + (char)c + "' not allowed for PUBLIC ID");
2539 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2542 // The reader is positioned on the first character
\r
2544 internal string ReadName ()
\r
2546 return ReadNameOrNmToken(false);
\r
2549 // The reader is positioned on the first character
\r
2551 private string ReadNmToken ()
\r
2553 return ReadNameOrNmToken(true);
\r
2556 private string ReadNameOrNmToken(bool isNameToken)
\r
2558 int ch = PeekChar ();
\r
2560 if (!XmlConstructs.IsName ((char) ch))
\r
2561 throw ReaderError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
\r
2564 if (!XmlConstructs.IsNameStart ((char) PeekChar ()))
\r
2565 throw ReaderError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
\r
2570 AppendNameChar (ReadChar ());
2572 while (XmlConstructs.IsName (PeekChar ())) {
2573 AppendNameChar (ReadChar ());
2576 return CreateNameString ();
2579 // Read the next character and compare it against the
2580 // specified character.
2581 private void Expect (int expected)
2583 int ch = ReadChar ();
2585 if (ch != expected) {
2588 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2596 private void Expect (string expected)
2598 int len = expected.Length;
2599 for(int i=0; i< len; i++)
2600 Expect (expected[i]);
2603 // Does not consume the first non-whitespace character.
2604 private void SkipWhitespace ()
2606 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
2607 while (XmlConstructs.IsSpace (PeekChar ()))
2611 private bool ReadWhitespace ()
2614 int ch = PeekChar ();
2616 AppendValueChar (ReadChar ());
2617 } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
2619 if (ch != -1 && ch != '<')
2622 SetProperties (XmlNodeType.Whitespace,
2625 CreateValueString (),
2628 return (PeekChar () != -1);
2631 // read entity reference from attribute string and if parsable then return the value.
2632 private string ReadAttributeValueReference ()
2634 int endEntityPosition = attributeString.IndexOf(';',
2636 string entityName = attributeString.Substring (attributeValuePos + 1,
2637 endEntityPosition - attributeValuePos - 1);
2639 attributeValuePos = endEntityPosition + 1;
2641 if(entityName [0] == '#') {
2644 if(entityName [1] == 'x') {
2646 c = (char) int.Parse ("0" + entityName.Substring (2),
2647 System.Globalization.NumberStyles.HexNumber);
2650 c = (char) int.Parse (entityName.Substring (1));
2652 return c.ToString();
2657 case "lt": return "<";
2658 case "gt": return ">";
2659 case "amp": return "&";
2660 case "quot": return "\"";
2661 case "apos": return "'";
2662 default: return null;
2667 private string UnescapeAttributeValue (string unresolved)
2669 if(unresolved == null) return null;
2671 // trim start/end edge of quotation character.
2672 return Dereference (unresolved.Substring (1, unresolved.Length - 2));
2675 private string Dereference (string unresolved)
2677 StringBuilder resolved = new StringBuilder();
2679 int next = unresolved.IndexOf ('&');
2685 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
2686 int endPos = unresolved.IndexOf (';', next+1);
2688 unresolved.Substring (next + 1, endPos - next - 1);
2689 if(entityName [0] == '#') {
2692 if(entityName [1] == 'x') {
2694 c = (char) int.Parse ("0" + entityName.Substring (2),
2695 System.Globalization.NumberStyles.HexNumber);
2698 c = (char) int.Parse (entityName.Substring (1));
2700 resolved.Append (c);
2702 switch(entityName) {
2703 case "lt": resolved.Append ("<"); break;
2704 case "gt": resolved.Append (">"); break;
2705 case "amp": resolved.Append ("&"); break;
2706 case "quot": resolved.Append ("\""); break;
2707 case "apos": resolved.Append ("'"); break;
2708 // With respect to "Value", MS document is helpless
2709 // and the implemention returns inconsistent value
2710 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
2711 default: resolved.Append ("&" + entityName + ";"); break;
2715 if(pos > unresolved.Length)
2717 next = unresolved.IndexOf('&', pos);
2719 resolved.Append (unresolved.Substring(pos));
2721 return resolved.ToString();