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.
25 using System.Collections;
28 using System.Xml.Schema;
30 using Mono.Xml.Native;
34 public class XmlTextReader : XmlReader, IXmlLineInfo
38 protected XmlTextReader ()
42 public XmlTextReader (Stream input)
43 : this (new XmlStreamReader (input))
47 public XmlTextReader (string url)
48 : this(url, new NameTable ())
52 public XmlTextReader (TextReader input)
53 : this (input, new NameTable ())
57 protected XmlTextReader (XmlNameTable nt)
58 : this (String.Empty, null, XmlNodeType.None, null)
62 public XmlTextReader (Stream input, XmlNameTable nt)
63 : this(new XmlStreamReader (input), nt)
67 public XmlTextReader (string url, Stream input)
68 : this (url, new XmlStreamReader (input))
72 public XmlTextReader (string url, TextReader input)
73 : this (url, input, new NameTable ())
77 public XmlTextReader (string url, XmlNameTable nt)
78 : this (url, new XmlStreamReader (url, null, null), nt)
82 public XmlTextReader (TextReader input, XmlNameTable nt)
83 : this (String.Empty, input, nt)
87 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
88 : this (context != null ? context.BaseURI : String.Empty,
89 new XmlStreamReader (xmlFragment),
95 public XmlTextReader (string url, Stream input, XmlNameTable nt)
96 : this (url, new XmlStreamReader (input), nt)
100 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
101 : this (url, input, XmlNodeType.Document, null)
105 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
106 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
107 : this (context != null ? context.BaseURI : String.Empty,
108 new StringReader (xmlFragment),
114 // TODO still remains as described at head of this file,
115 // but it might not be TODO of the constructors...
116 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
118 InitializeContext (url, context, fragment, fragType);
125 public override int AttributeCount
127 get { return attributes.Count; }
130 public override string BaseURI
132 get { return parserContext.BaseURI; }
135 public override int Depth
142 public Encoding Encoding
144 get { return parserContext.Encoding; }
147 public override bool EOF
152 readState == ReadState.EndOfFile ||
153 readState == ReadState.Closed;
157 public override bool HasValue
160 if (this.valueBuilderAvailable)
161 return valueBuilder.Length != 0;
163 return value != String.Empty;
167 public override bool IsDefault
171 // XmlTextReader does not expand default attributes.
176 public override bool IsEmptyElement
178 get { return isEmptyElement; }
181 public override string this [int i]
183 get { return GetAttribute (i); }
186 public override string this [string name]
188 get { return GetAttribute (name); }
191 public override string this [string localName, string namespaceName]
193 get { return GetAttribute (localName, namespaceName); }
196 public int LineNumber
198 get { return currentInput.LineNumber; }
201 public int LinePosition
203 get { return currentInput.LinePosition; }
206 public override string LocalName
208 get { return localName; }
211 public override string Name
216 public bool Namespaces
218 get { return namespaces; }
220 if (readState != ReadState.Initial)
221 throw new InvalidOperationException ("Namespaces have to be set before reading.");
226 public override string NamespaceURI
228 get { return namespaceURI; }
231 public override XmlNameTable NameTable
233 get { return parserContext.NameTable; }
236 public override XmlNodeType NodeType
238 get { return nodeType; }
242 public bool Normalization
244 get { return normalization; }
245 set { normalization = value; }
248 public override string Prefix
250 get { return prefix; }
253 public override char QuoteChar
256 // value string holds attribute quotation char.
257 if (NodeType == XmlNodeType.Attribute)
264 public override ReadState ReadState
266 get { return readState; }
269 public override string Value
274 if (valueBuilderAvailable)
275 v = valueBuilder.ToString ();
276 if(NodeType == XmlNodeType.Attribute)
277 return UnescapeAttributeValue(v);
283 public WhitespaceHandling WhitespaceHandling
285 get { return whitespaceHandling; }
286 set { whitespaceHandling = value; }
289 public override string XmlLang
291 get { return parserContext.XmlLang; }
294 public XmlResolver XmlResolver
296 set { resolver = value; }
299 public override XmlSpace XmlSpace
301 get { return parserContext.XmlSpace; }
308 public override void Close ()
310 readState = ReadState.Closed;
311 foreach (XmlParserInput input in parserInputStack.ToArray ())
313 this.currentInput.Close ();
316 public override string GetAttribute (int i)
318 if (i > attributes.Count)
319 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
321 return UnescapeAttributeValue (attributes [orderedAttributes [i]] as string);
324 // MS.NET 1.0 documentation says that this method returns String.Empty for
325 // not-exist attribute, but in fact it returns null.
326 // That description is corrected in MS.NET 1.1 documentation.
327 public override string GetAttribute (string name)
329 return UnescapeAttributeValue (attributes [name] as string);
332 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
334 for(int i = 0; i < orderedAttributes.Count; i++)
336 string thisName = (string) orderedAttributes [i];
338 int indexOfColon = thisName.IndexOf (':');
340 if (indexOfColon != -1) {
341 string thisLocalName = thisName.Substring (indexOfColon + 1);
343 if (localName == thisLocalName) {
344 string thisPrefix = thisName.Substring (0, indexOfColon);
345 string thisNamespaceURI = LookupNamespace (thisPrefix);
347 if (namespaceURI == thisNamespaceURI)
350 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
356 internal XmlParserContext GetInternalParserContext ()
358 return parserContext;
361 public override string GetAttribute (string localName, string namespaceURI)
363 int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
366 return UnescapeAttributeValue (attributes [orderedAttributes [idx]] as string);
370 public TextReader GetRemainder ()
372 throw new NotImplementedException ();
375 bool IXmlLineInfo.HasLineInfo ()
380 public override string LookupNamespace (string prefix)
382 return parserContext.NamespaceManager.LookupNamespace (prefix);
385 public override void MoveToAttribute (int i)
389 if (attributes == null || orderedAttributes.Count < i || i < 0)
390 throw new ArgumentOutOfRangeException ("attribute index out of range.");
392 if (orderedAttributesEnumerator == null) {
396 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
397 for (int n=0; n<=i; n++)
398 orderedAttributesEnumerator.MoveNext();
400 string name = orderedAttributes [i] as string;
401 string value = attributes [name] as string;
403 XmlNodeType.Attribute, // nodeType
405 false, // isEmptyElement
407 false // clearAttributes
409 attributeValuePos = 0;
412 public override bool MoveToAttribute (string name)
417 if (attributes == null)
420 if (orderedAttributesEnumerator == null) {
424 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
425 while (orderedAttributesEnumerator.MoveNext ()) {
426 if(name == orderedAttributesEnumerator.Current as string) {
433 string value = attributes [name] as string;
435 XmlNodeType.Attribute, // nodeType
437 false, // isEmptyElement
439 false // clearAttributes
441 attributeValuePos = 0;
447 public override bool MoveToAttribute (string localName, string namespaceName)
451 if (attributes == null)
454 int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
457 MoveToAttribute (idx);
461 public override bool MoveToElement ()
463 if (orderedAttributesEnumerator != null) {
464 orderedAttributesEnumerator = null;
466 RestoreProperties ();
473 public override bool MoveToFirstAttribute ()
476 return MoveToNextAttribute ();
479 public override bool MoveToNextAttribute ()
481 if (attributes == null)
484 if (orderedAttributesEnumerator == null) {
486 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
489 if (orderedAttributesEnumerator.MoveNext ()) {
490 string name = orderedAttributesEnumerator.Current as string;
491 string value = attributes [name] as string;
493 XmlNodeType.Attribute, // nodeType
495 false, // isEmptyElement
497 false // clearAttributes
499 attributeValuePos = 0;
506 public override bool Read ()
509 isPropertySaved = false;
510 readState = ReadState.Interactive;
512 // It was moved from end of ReadStartTag ().
517 more = ReadContent ();
519 if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
520 currentState = XmlNodeType.EndElement;
521 if (maybeTextDecl != 0)
527 public override bool ReadAttributeValue ()
529 // 'attributeString' holds real string value (without their
530 // quotation characters).
532 // 'attributeValuePos' holds current position
533 // of 'attributeString' while iterating ReadAttribute().
535 // -1 if ReadAttributeValue() has already finished.
536 // 0 if ReadAttributeValue() ready to start reading.
537 // >0 if ReadAttributeValue() already got 1 or more values
539 // local 'refPosition' holds the position on the
540 // attributeString which may be used next time.
542 if (attributeValuePos < 0)
545 // If not started, then initialize attributeString when parsing is at start.
546 if (attributeValuePos == 0)
548 value.Substring (1, value.Length - 2);
550 // It occurs when attribute dully consists of entity reference.
551 if (attributeValuePos == attributeString.Length)
554 returnEntityReference = false;
555 value = String.Empty;
560 refPosition = attributeString.IndexOf ('&', attributeValuePos);
561 if (refPosition < 0) {
562 // Reached to the end of value string.
563 value += attributeString.Substring (attributeValuePos);
564 attributeValuePos = -1;
566 } else if (refPosition == attributeValuePos) {
567 string parsed = ReadAttributeValueReference ();
571 // Found that an entity reference starts from this point.
572 // reset position to after '&'.
573 attributeValuePos = refPosition;
574 if (value.Length <= 0) {
575 int endNamePos = attributeString.IndexOf (";", attributeValuePos);
576 value = attributeString.Substring (attributeValuePos+1, endNamePos - attributeValuePos - 1);
577 attributeValuePos += value.Length + 2;
578 returnEntityReference = true;
583 value += attributeString.Substring (attributeValuePos,
584 refPosition - attributeValuePos);
585 attributeValuePos = refPosition;
588 } while (++loop > 0);
590 if (returnEntityReference)
591 SetProperties (XmlNodeType.EntityReference,
597 SetProperties (XmlNodeType.Text,
607 public int ReadBase64 (byte [] buffer, int offset, int length)
609 throw new NotImplementedException ();
613 public int ReadBinHex (byte [] buffer, int offset, int length)
615 throw new NotImplementedException ();
619 public int ReadChars (char [] buffer, int offset, int length)
621 throw new NotImplementedException ();
624 public override string ReadInnerXml ()
626 if (readState != ReadState.Interactive)
630 case XmlNodeType.Attribute:
631 return value.Substring (1, value.Length - 2);
632 case XmlNodeType.Element:
636 int startDepth = depth;
638 if (innerXmlBuilder == null)
639 innerXmlBuilder = new StringBuilder ();
640 innerXmlBuilder.Length = 0;
644 if (NodeType ==XmlNodeType.None)
645 throw new XmlException ("unexpected end of xml.");
646 else if (NodeType == XmlNodeType.EndElement && depth == startDepth) {
651 innerXmlBuilder.Append (currentTag);
653 string xml = innerXmlBuilder.ToString ();
654 innerXmlBuilder.Length = 0;
656 case XmlNodeType.None:
657 // MS document is incorrect. Seems not to progress.
665 public override string ReadOuterXml ()
667 if (readState != ReadState.Interactive)
671 case XmlNodeType.Attribute:
672 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
673 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
674 case XmlNodeType.Element:
675 bool isEmpty = IsEmptyElement;
676 string startTag = currentTag.ToString ();
679 if (NodeType == XmlNodeType.Element && !isEmpty)
680 return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
682 return currentTag.ToString ();
683 case XmlNodeType.None:
684 // MS document is incorrect. Seems not to progress.
692 public override string ReadString ()
694 return ReadStringInternal ();
697 public void ResetState ()
702 public override void ResolveEntity ()
704 // XmlTextReaders don't resolve entities.
705 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
711 // Parsed DTD Objects
712 internal DTDObjectModel DTD;
717 private XmlParserContext parserContext;
719 private XmlParserInput currentInput;
720 private Stack parserInputStack;
721 private ReadState readState;
724 private int elementDepth;
725 private bool depthUp;
727 private bool popScope;
728 private Stack elementStack;
729 private bool allowMultipleRoot;
731 private XmlNodeType nodeType;
733 private string prefix;
734 private string localName;
735 private string namespaceURI;
736 private bool isEmptyElement;
737 private string value;
738 private StringBuilder valueBuilder;
739 private bool valueBuilderAvailable = false;
741 private bool isPropertySaved;
742 private XmlNodeType saveNodeType;
743 private string saveName;
744 private string savePrefix;
745 private string saveLocalName;
746 private string saveNamespaceURI;
747 private bool saveIsEmptyElement;
749 private Hashtable attributes;
750 private ArrayList orderedAttributes;
751 private IEnumerator orderedAttributesEnumerator;
753 private bool returnEntityReference;
754 private string entityReferenceName;
756 private char [] nameBuffer;
757 private int nameLength;
758 private int nameCapacity;
759 private const int initialNameCapacity = 256;
761 private StringBuilder valueBuffer;
763 // A buffer for ReadContent for ReadOuterXml
764 private StringBuilder currentTag {
766 return currentInput.CurrentMarkup;
770 private string attributeString;
771 private int attributeValuePos;
772 // This should be only referenced(used) by ReadInnerXml(). Kind of flyweight pattern.
773 private StringBuilder innerXmlBuilder;
775 // Parameter entity placeholder
776 private Hashtable parameterEntities;
777 private int dtdIncludeSect;
778 private bool isIntSubset;
780 // State machine attribute.
781 // XmlDeclaration: after the first node.
782 // DocumentType: after doctypedecl
783 // Element: inside document element
784 // EndElement: after document element
785 private XmlNodeType currentState;
786 private int maybeTextDecl;
788 // These values are never re-initialized.
789 private XmlResolver resolver = new XmlUrlResolver ();
790 private bool namespaces = true;
791 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
792 private bool normalization = false;
796 readState = ReadState.Initial;
797 currentState = XmlNodeType.None;
799 allowMultipleRoot = false;
805 parserInputStack = new Stack ();
806 elementStack = new Stack();
808 nodeType = XmlNodeType.None;
810 prefix = String.Empty;
811 localName = string.Empty;
812 isEmptyElement = false;
813 value = String.Empty;
815 attributes = new Hashtable ();
816 attributeString = String.Empty;
817 orderedAttributes = new ArrayList ();
818 orderedAttributesEnumerator = null;
820 returnEntityReference = false;
821 entityReferenceName = String.Empty;
823 nameBuffer = new char [initialNameCapacity];
825 nameCapacity = initialNameCapacity;
827 valueBuffer = new StringBuilder (8192);
828 parameterEntities = new Hashtable ();
831 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
833 parserContext = context;
834 if (context == null) {
835 XmlNameTable nt = new NameTable ();
836 parserContext = new XmlParserContext (nt,
837 new XmlNamespaceManager (nt),
842 if (url != null && url != String.Empty) {
843 string path = Path.GetFullPath ("./a");
844 Uri uri = new Uri (new Uri (path), url);
845 parserContext.BaseURI = uri.ToString ();
851 case XmlNodeType.Attribute:
852 value = String.Format ("{0}{1}{0}", "'", fragment.ReadToEnd ().Replace ("'", "'"));
854 case XmlNodeType.Element:
855 currentState = XmlNodeType.Element;
856 allowMultipleRoot = true;
858 case XmlNodeType.Document:
861 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
864 this.currentInput = new XmlParserInput (fragment, url);
865 StreamReader sr = fragment as StreamReader;
868 // Use this method rather than setting the properties
869 // directly so that all the necessary properties can
870 // be changed in harmony with each other. Maybe the
871 // fields should be in a seperate class to help enforce
873 private void SetProperties (
874 XmlNodeType nodeType,
878 bool clearAttributes)
880 this.nodeType = nodeType;
882 this.isEmptyElement = isEmptyElement;
884 this.elementDepth = depth;
885 this.valueBuilderAvailable = false;
891 int indexOfColon = name.IndexOf (':');
893 if (indexOfColon == -1) {
894 prefix = String.Empty;
897 prefix = name.Substring (0, indexOfColon);
898 localName = name.Substring (indexOfColon + 1);
901 prefix = String.Empty;
906 case XmlNodeType.Attribute:
907 if (prefix == string.Empty) namespaceURI = string.Empty;
908 else namespaceURI = LookupNamespace (prefix);
909 if (localName == "xmlns" && prefix == "")
910 namespaceURI = "http://www.w3.org/2000/xmlns/";
913 case XmlNodeType.Element:
914 case XmlNodeType.EndElement:
915 namespaceURI = LookupNamespace (prefix);
923 private void SetProperties (
924 XmlNodeType nodeType,
928 bool clearAttributes) {
929 SetProperties (nodeType, name, isEmptyElement, (string)null, clearAttributes);
930 this.valueBuilderAvailable = true;
931 this.valueBuilder = value;
934 private void SaveProperties ()
936 // If already saved, then return.
940 saveNodeType = nodeType;
943 saveLocalName = localName;
944 saveNamespaceURI = namespaceURI;
945 saveIsEmptyElement = isEmptyElement;
946 // An element's value is always String.Empty.
947 isPropertySaved = true;
950 private void RestoreProperties ()
952 nodeType = saveNodeType;
955 localName = saveLocalName;
956 namespaceURI = saveNamespaceURI;
957 isEmptyElement = saveIsEmptyElement;
958 value = String.Empty;
959 isPropertySaved = false;
962 private void AddAttribute (string name, string value)
964 if (attributes.ContainsKey (name))
965 throw new XmlException (this as IXmlLineInfo,
966 String.Format ("Attribute {0} already exists.", name));
967 attributes.Add (name, value);
968 orderedAttributes.Add (name);
971 private void ClearAttributes ()
973 if (attributes.Count > 0) {
975 orderedAttributes.Clear ();
978 orderedAttributesEnumerator = null;
981 private int PeekChar ()
983 return currentInput.PeekChar ();
986 private int ReadChar ()
988 return currentInput.ReadChar ();
991 // This should really keep track of some state so
992 // that it's not possible to have more than one document
993 // element or text outside of the document element.
994 private bool ReadContent ()
996 currentTag.Length = 0;
998 parserContext.NamespaceManager.PopScope ();
1002 if (returnEntityReference) {
1003 SetEntityReferenceProperties ();
1005 switch (PeekChar ()) {
1010 case '\r': goto case ' ';
1011 case '\n': goto case ' ';
1012 case '\t': goto case ' ';
1014 if (whitespaceHandling == WhitespaceHandling.All ||
1015 whitespaceHandling == WhitespaceHandling.Significant)
1016 return ReadWhitespace ();
1019 return ReadContent ();
1022 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1023 readState = ReadState.EndOfFile;
1025 XmlNodeType.None, // nodeType
1026 String.Empty, // name
1027 false, // isEmptyElement
1028 String.Empty, // value
1029 true // clearAttributes
1037 return this.ReadState != ReadState.EndOfFile;
1040 private void SetEntityReferenceProperties ()
1043 XmlNodeType.EntityReference, // nodeType
1044 entityReferenceName, // name
1045 false, // isEmptyElement
1046 String.Empty, // value
1047 true // clearAttributes
1050 returnEntityReference = false;
1051 entityReferenceName = String.Empty;
1054 // The leading '<' has already been consumed.
1055 private void ReadTag ()
1057 switch (PeekChar ())
1065 ReadProcessingInstruction ();
1077 // The leading '<' has already been consumed.
1078 private void ReadStartTag ()
1080 if (currentState == XmlNodeType.EndElement)
1081 throw new XmlException (this as IXmlLineInfo,
1082 "Element cannot appear in this state.");
1083 currentState = XmlNodeType.Element;
1085 parserContext.NamespaceManager.PushScope ();
1087 string name = ReadName ();
1088 if (currentState == XmlNodeType.EndElement)
1089 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1091 bool isEmptyElement = false;
1096 if (XmlConstructs.IsNameStart (PeekChar ()))
1097 ReadAttributes (false);
1099 string baseUri = GetAttribute ("xml:base");
1100 if (baseUri != null)
1101 parserContext.BaseURI = baseUri;
1102 string xmlLang = GetAttribute ("xml:lang");
1103 if (xmlLang != null)
1104 parserContext.XmlLang = xmlLang;
1105 string xmlSpaceAttr = GetAttribute ("xml:space");
1106 if (xmlSpaceAttr != null) {
1107 if (xmlSpaceAttr == "preserve")
1108 parserContext.XmlSpace = XmlSpace.Preserve;
1109 else if (xmlSpaceAttr == "default")
1110 parserContext.XmlSpace = XmlSpace.Default;
1112 throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1114 if (PeekChar () == '/') {
1116 isEmptyElement = true;
1121 elementStack.Push (name);
1122 parserContext.PushScope ();
1128 XmlNodeType.Element, // nodeType
1130 isEmptyElement, // isEmptyElement
1131 String.Empty, // value
1132 false // clearAttributes
1136 // The reader is positioned on the first character
1137 // of the element's name.
1138 private void ReadEndTag ()
1140 if (currentState != XmlNodeType.Element)
1141 throw new XmlException (this as IXmlLineInfo,
1142 "End tag cannot appear in this state.");
1144 string name = ReadName ();
1145 if (elementStack.Count == 0)
1146 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1147 string expected = (string)elementStack.Pop();
1148 if (expected != name)
1149 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1150 parserContext.PopScope ();
1158 XmlNodeType.EndElement, // nodeType
1160 false, // isEmptyElement
1161 String.Empty, // value
1162 true // clearAttributes
1168 private void AppendNameChar (int ch)
1170 CheckNameCapacity ();
1171 nameBuffer [nameLength++] = (char)ch;
1174 private void CheckNameCapacity ()
1176 if (nameLength == nameCapacity) {
1177 nameCapacity = nameCapacity * 2;
1178 char [] oldNameBuffer = nameBuffer;
1179 nameBuffer = new char [nameCapacity];
1180 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1184 private string CreateNameString ()
1186 return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1189 private void AppendValueChar (int ch)
1191 valueBuffer.Append ((char)ch);
1194 private string CreateValueString ()
1196 return valueBuffer.ToString ();
1199 private void ClearValueBuffer ()
1201 valueBuffer.Length = 0;
1204 // The reader is positioned on the first character
1206 private void ReadText (bool cleanValue)
1208 if (currentState != XmlNodeType.Element)
1209 throw new XmlException (this as IXmlLineInfo,
1210 "Text node cannot appear in this state.");
1213 ClearValueBuffer ();
1215 int ch = PeekChar ();
1217 while (ch != '<' && ch != -1) {
1220 if (ReadReference (false))
1223 AppendValueChar (ReadChar ());
1228 if (returnEntityReference && valueBuffer.Length == 0) {
1229 SetEntityReferenceProperties ();
1232 XmlNodeType.Text, // nodeType
1233 String.Empty, // name
1234 false, // isEmptyElement
1235 valueBuffer, // value
1236 true // clearAttributes
1241 // The leading '&' has already been consumed.
1242 // Returns true if the entity reference isn't a simple
1243 // character reference or one of the predefined entities.
1244 // This allows the ReadText method to break so that the
1245 // next call to Read will return the EntityReference node.
1246 private bool ReadReference (bool ignoreEntityReferences)
1248 if (PeekChar () == '#') {
1250 ReadCharacterReference ();
1252 ReadEntityReference (ignoreEntityReferences);
1254 return returnEntityReference;
1257 private void ReadCharacterReference ()
1261 if (PeekChar () == 'x') {
1264 while (PeekChar () != ';' && PeekChar () != -1) {
1265 int ch = ReadChar ();
1267 if (ch >= '0' && ch <= '9')
1268 value = (value << 4) + ch - '0';
1269 else if (ch >= 'A' && ch <= 'F')
1270 value = (value << 4) + ch - 'A' + 10;
1271 else if (ch >= 'a' && ch <= 'f')
1272 value = (value << 4) + ch - 'a' + 10;
1274 throw new XmlException (this as IXmlLineInfo,
1276 "invalid hexadecimal digit: {0} (#x{1:X})",
1281 while (PeekChar () != ';' && PeekChar () != -1) {
1282 int ch = ReadChar ();
1284 if (ch >= '0' && ch <= '9')
1285 value = value * 10 + ch - '0';
1287 throw new XmlException (this as IXmlLineInfo,
1289 "invalid decimal digit: {0} (#x{1:X})",
1297 AppendValueChar (value);
1300 private void ReadEntityReference (bool ignoreEntityReferences)
1304 int ch = PeekChar ();
1306 while (ch != ';' && ch != -1) {
1307 AppendNameChar (ReadChar ());
1313 string name = CreateNameString ();
1318 AppendValueChar ('<');
1321 AppendValueChar ('>');
1324 AppendValueChar ('&');
1327 AppendValueChar ('\'');
1330 AppendValueChar ('"');
1333 if (ignoreEntityReferences) {
1334 AppendValueChar ('&');
1336 foreach (char ch2 in name) {
1337 AppendValueChar (ch2);
1340 AppendValueChar (';');
1342 returnEntityReference = true;
1343 entityReferenceName = name;
1349 // The reader is positioned on the first character of
1350 // the attribute name.
1351 private void ReadAttributes (bool allowPIEnd)
1354 bool requireWhitespace = false;
1356 if (!SkipWhitespace () && requireWhitespace)
1357 throw new XmlException ("Unexpected token. Name is required here.");
1358 string name = ReadName ();
1362 string value = ReadAttribute ();
1364 if (name == "xmlns")
1365 parserContext.NamespaceManager.AddNamespace (String.Empty, UnescapeAttributeValue (value));
1366 else if (name.StartsWith ("xmlns:"))
1367 parserContext.NamespaceManager.AddNamespace (name.Substring (6), UnescapeAttributeValue (value));
1369 AddAttribute (name, value);
1371 if (XmlConstructs.IsSpace (PeekChar ()))
1374 requireWhitespace = true;
1375 peekChar = PeekChar ();
1376 if (peekChar == '?' && allowPIEnd)
1378 } while (peekChar != '/' && peekChar != '>' && peekChar != -1);
1381 // The reader is positioned on the quote character.
1382 // *Keeps quote char* to value to get_QuoteChar() correctly.
1383 private string ReadAttribute ()
1385 ClearValueBuffer ();
1387 int quoteChar = ReadChar ();
1389 if (quoteChar != '\'' && quoteChar != '\"')
1390 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1392 AppendValueChar (quoteChar);
1394 while (PeekChar () != quoteChar) {
1395 int ch = ReadChar ();
1400 throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1402 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1404 AppendValueChar (ch);
1409 ReadChar (); // quoteChar
1410 AppendValueChar (quoteChar);
1412 return CreateValueString ();
1415 // The reader is positioned on the first character
1418 // It may be xml declaration or processing instruction.
1419 private void ReadProcessingInstruction ()
1421 string target = ReadName ();
1422 if (target == "xml") {
1423 ReadXmlDeclaration ();
1426 if (currentState == XmlNodeType.None)
1427 currentState = XmlNodeType.XmlDeclaration;
1431 ClearValueBuffer ();
1433 while (PeekChar () != -1) {
1434 int ch = ReadChar ();
1436 if (ch == '?' && PeekChar () == '>') {
1441 AppendValueChar ((char)ch);
1445 XmlNodeType.ProcessingInstruction, // nodeType
1447 false, // isEmptyElement
1448 valueBuffer, // value
1449 true // clearAttributes
1453 // The reader is positioned after "<?xml "
1454 private void ReadXmlDeclaration ()
1456 if (currentState != XmlNodeType.None) {
1457 if (maybeTextDecl == 0)
1458 throw new XmlException (this as IXmlLineInfo,
1459 "XML declaration cannot appear in this state.");
1461 currentState = XmlNodeType.XmlDeclaration;
1465 ReadAttributes (true); // They must have "version."
1468 if (maybeTextDecl != 0)
1469 if (this ["standalone"] != null)
1470 throw new XmlException (this as IXmlLineInfo,
1471 "Invalid text declaration.");
1475 XmlNodeType.XmlDeclaration, // nodeType
1477 false, // isEmptyElement
1478 currentInput.CurrentMarkup.ToString (6, currentInput.CurrentMarkup.Length - 6), // value
1479 false // clearAttributes
1483 // The reader is positioned on the first character after
1484 // the leading '<!'.
1485 private void ReadDeclaration ()
1487 int ch = PeekChar ();
1507 // The reader is positioned on the first character after
1508 // the leading '<!--'.
1509 private void ReadComment ()
1511 if (currentState == XmlNodeType.None)
1512 currentState = XmlNodeType.XmlDeclaration;
1514 ClearValueBuffer ();
1516 while (PeekChar () != -1) {
1517 int ch = ReadChar ();
1519 if (ch == '-' && PeekChar () == '-') {
1522 if (PeekChar () != '>')
1523 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
1529 AppendValueChar ((char)ch);
1533 XmlNodeType.Comment, // nodeType
1534 String.Empty, // name
1535 false, // isEmptyElement
1536 valueBuffer, // value
1537 true // clearAttributes
1541 // The reader is positioned on the first character after
1542 // the leading '<![CDATA['.
1543 private void ReadCDATA ()
1545 if (currentState != XmlNodeType.Element)
1546 throw new XmlException (this as IXmlLineInfo,
1547 "CDATA section cannot appear in this state.");
1549 ClearValueBuffer ();
1553 while (PeekChar () != -1) {
1558 if (ch == ']' && PeekChar () == ']') {
1559 ch = ReadChar (); // ']'
1561 if (PeekChar () == '>') {
1566 // AppendValueChar (']');
1567 // AppendValueChar (']');
1568 // ch = ReadChar ();
1572 AppendValueChar ((char)ch);
1576 XmlNodeType.CDATA, // nodeType
1577 String.Empty, // name
1578 false, // isEmptyElement
1579 valueBuffer, // value
1580 true // clearAttributes
1584 // The reader is positioned on the first character after
1585 // the leading '<!DOCTYPE'.
1586 private void ReadDoctypeDecl ()
1588 switch (currentState) {
1589 case XmlNodeType.DocumentType:
1590 case XmlNodeType.Element:
1591 case XmlNodeType.EndElement:
1592 throw new XmlException (this as IXmlLineInfo,
1593 "Document type cannot appear in this state.");
1595 currentState = XmlNodeType.DocumentType;
1597 string doctypeName = null;
1598 string publicId = String.Empty;
1599 string systemId = String.Empty;
1600 int intSubsetStartLine = 0;
1601 int intSubsetStartColumn = 0;
1604 doctypeName = ReadName ();
1609 systemId = ReadSystemLiteral (true);
1612 publicId = ReadPubidLiteral ();
1614 systemId = ReadSystemLiteral (false);
1620 if(PeekChar () == '[')
1622 // read markupdecl etc. or end of decl
1624 intSubsetStartLine = this.LineNumber;
1625 intSubsetStartColumn = this.LinePosition;
1626 int startPos = currentTag.Length;
1628 ReadInternalSubset ();
1629 isIntSubset = false;
1630 int endPos = currentTag.Length - 1;
1631 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1633 // end of DOCTYPE decl.
1637 GenerateDTDObjectModel (doctypeName, publicId,
1638 systemId, parserContext.InternalSubset,
1639 intSubsetStartLine, intSubsetStartColumn);
1641 // set properties for <!DOCTYPE> node
1643 XmlNodeType.DocumentType, // nodeType
1644 doctypeName, // name
1645 false, // isEmptyElement
1646 parserContext.InternalSubset, // value
1647 true // clearAttributes
1651 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
1652 string systemId, string internalSubset)
1654 return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
1657 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
1658 string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
1661 DTD = new DTDObjectModel (); // merges both internal and external subsets in the meantime,
1663 int originalParserDepth = parserInputStack.Count;
1664 if (internalSubset != null && internalSubset.Length > 0) {
1665 XmlParserInput original = currentInput;
1666 currentInput = new XmlParserInput (new StringReader (internalSubset), BaseURI, intSubsetStartLine, intSubsetStartColumn);
1668 CompileDTDSubset ();
1669 if (PeekChar () == -1 && parserInputStack.Count > 0)
1671 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth);
1672 if (dtdIncludeSect != 0)
1673 throw new XmlException (this as IXmlLineInfo,"INCLUDE section is not ended correctly.");
1674 currentInput = original;
1676 if (systemId != null && systemId != String.Empty && resolver != null) {
1677 PushParserInput (systemId);
1679 this.CompileDTDSubset ();
1680 if (PeekChar () == -1 && parserInputStack.Count > 1)
1682 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth + 1);
1689 private void PushParserInput (string url)
1693 baseUri = new Uri (BaseURI);
1694 } catch (UriFormatException) {
1697 Uri absUri = resolver.ResolveUri (baseUri, url);
1698 string absPath = absUri.ToString ();
1700 foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1701 if (i.BaseURI == absPath)
1702 throw new XmlException (this as IXmlLineInfo, "Nested inclusion is not allowed: " + url);
1704 parserInputStack.Push (currentInput);
1705 currentInput = new XmlParserInput (new XmlStreamReader (url, false, resolver, BaseURI), absPath);
1706 parserContext.PushScope ();
1707 parserContext.BaseURI = absPath;
1712 private void PopParserInput ()
1714 currentInput = parserInputStack.Pop () as XmlParserInput;
1715 parserContext.PopScope ();
1718 private enum DtdInputState
1731 private class DtdInputStateStack
1733 Stack intern = new Stack ();
1734 public DtdInputStateStack ()
1736 Push (DtdInputState.Free);
1739 public DtdInputState Peek ()
1741 return (DtdInputState) intern.Peek ();
1744 public DtdInputState Pop ()
1746 return (DtdInputState) intern.Pop ();
1749 public void Push (DtdInputState val)
1756 DtdInputStateStack stateStack = new DtdInputStateStack ();
1757 DtdInputState State {
1758 get { return stateStack.Peek (); }
1761 // Simply read but not generate any result.
1762 private void ReadInternalSubset ()
1764 bool continueParse = true;
1766 while (continueParse) {
1767 switch (ReadChar ()) {
1770 case DtdInputState.Free:
1771 continueParse = false;
1773 case DtdInputState.InsideDoubleQuoted:
1775 case DtdInputState.InsideSingleQuoted:
1778 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
1782 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
1784 if (State == DtdInputState.InsideDoubleQuoted ||
1785 State == DtdInputState.InsideSingleQuoted)
1786 continue; // well-formed
1787 switch (ReadChar ()) {
1789 stateStack.Push (DtdInputState.PI);
1792 switch (ReadChar ()) {
1794 switch (ReadChar ()) {
1797 stateStack.Push (DtdInputState.ElementDecl);
1801 stateStack.Push (DtdInputState.EntityDecl);
1804 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
1809 stateStack.Push (DtdInputState.AttlistDecl);
1813 stateStack.Push (DtdInputState.NotationDecl);
1817 stateStack.Push (DtdInputState.Comment);
1822 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
1826 if (State == DtdInputState.InsideSingleQuoted)
1828 else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
1829 stateStack.Push (DtdInputState.InsideSingleQuoted);
1832 if (State == DtdInputState.InsideDoubleQuoted)
1834 else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
1835 stateStack.Push (DtdInputState.InsideDoubleQuoted);
1839 case DtdInputState.ElementDecl:
1840 goto case DtdInputState.NotationDecl;
1841 case DtdInputState.AttlistDecl:
1842 goto case DtdInputState.NotationDecl;
1843 case DtdInputState.EntityDecl:
1844 goto case DtdInputState.NotationDecl;
1845 case DtdInputState.NotationDecl:
1848 case DtdInputState.InsideDoubleQuoted:
1850 case DtdInputState.InsideSingleQuoted:
1851 continue; // well-formed
1852 case DtdInputState.Comment:
1855 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
1859 if (State == DtdInputState.PI) {
1860 if (ReadChar () == '>')
1865 if (State == DtdInputState.Comment) {
1866 if (PeekChar () == '-') {
1874 if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
1875 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
1881 // Read any one of following:
1882 // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1883 // PI, Comment, Parameter Entity, or doctype termination char(']')
1885 // returns a node of some nodeType or null, setting nodeType.
1886 // (if None then ']' was found.)
1887 private void CompileDTDSubset()
1893 nodeType = XmlNodeType.None;
1896 // It affects on entity references' well-formedness
1898 DTD.InternalSubsetHasPEReference = true;
1906 // Only read, no store.
1907 ReadProcessingInstruction ();
1910 CompileDeclaration ();
1913 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<' character.");
1923 throw new XmlException (this as IXmlLineInfo,String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
1927 private void CompileDeclaration ()
1929 nodeType = XmlNodeType.DocumentType; // Hack!!
1934 // Only read, no store.
1944 if (PeekChar () == '%') {
1946 if (!XmlConstructs.IsSpace (PeekChar ())) {
1949 // throw new XmlException (this as IXmlLineInfo,"expected whitespace between '%' and name.");
1953 if (XmlConstructs.IsName (PeekChar ()))
1954 ReadParameterEntityDecl ();
1956 throw new XmlException (this as IXmlLineInfo,"expected name character");
1960 DTDEntityDeclaration ent = ReadEntityDecl ();
1961 if (DTD.EntityDecls [ent.Name] == null)
1962 DTD.EntityDecls.Add (ent.Name, ent);
1966 DTDElementDeclaration el = ReadElementDecl ();
1967 DTD.ElementDecls.Add (el.Name, el);
1970 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1975 DTDAttListDeclaration atl = ReadAttListDecl ();
1976 // if (DTD.AttListDecls.ContainsKey (atl.Name))
1977 DTD.AttListDecls.Add (atl.Name, atl);
1981 DTDNotationDeclaration not = ReadNotationDecl ();
1982 DTD.NotationDecls.Add (not.Name, not);
1985 // conditional sections
1990 switch (ReadChar ()) {
2004 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!' characters.");
2008 private void ReadIgnoreSect ()
2013 int dtdIgnoreSect = 1;
2014 while (dtdIgnoreSect > 0) {
2015 switch (skip ? PeekChar () : ReadChar ()) {
2017 throw new XmlException (this as IXmlLineInfo,"Unexpected IGNORE section end.");
2019 if (ReadChar () == '!' && ReadChar () == '[')
2023 if (ReadChar () == ']') {
2024 if (ReadChar () == '>')
2035 // The reader is positioned on the head of the name.
2036 private DTDElementDeclaration ReadElementDecl ()
2038 DTDElementDeclaration decl = new DTDElementDeclaration (DTD);
2041 decl.Name = ReadName ();
2044 ReadContentSpec (decl);
2046 // This expanding is only allowed as a non-validating parser.
2052 // read 'children'(BNF) of contentspec
2053 private void ReadContentSpec (DTDElementDeclaration decl)
2058 decl.IsEmpty = true;
2066 DTDContentModel model = decl.ContentModel;
2070 if(PeekChar () == '#') {
2071 // Mixed Contents. "#PCDATA" must appear first.
2072 decl.IsMixedContent = true;
2077 while(PeekChar () != ')') {
2082 DTDContentModel elem = new DTDContentModel (DTD, decl.Name);
2083 model.ElementName = ReadName ();
2084 model.ChildModels.Add (elem);
2089 if (model.ChildModels.Count > 0) {
2091 model.Occurence = DTDOccurence.ZeroOrMore;
2093 else if (PeekChar () == '*')
2096 // Non-Mixed Contents
2097 model.ChildModels.Add (ReadCP (decl));
2100 do { // copied from ReadCP() ...;-)
2103 if(PeekChar ()=='|') {
2105 model.OrderType = DTDContentOrderType.Or;
2108 model.ChildModels.Add (ReadCP (decl));
2111 else if(PeekChar () == ',')
2114 model.OrderType = DTDContentOrderType.Seq;
2117 model.ChildModels.Add (ReadCP (decl));
2129 model.Occurence = DTDOccurence.Optional;
2133 model.Occurence = DTDOccurence.ZeroOrMore;
2137 model.Occurence = DTDOccurence.OneOrMore;
2148 // Read 'cp' (BNF) of contentdecl (BNF)
2149 private DTDContentModel ReadCP (DTDElementDeclaration elem)
2151 DTDContentModel model = null;
2153 if(PeekChar () == '(') {
2154 model = new DTDContentModel (DTD, elem.Name);
2157 model.ChildModels.Add (ReadCP (elem));
2162 if(PeekChar ()=='|') {
2164 model.OrderType = DTDContentOrderType.Or;
2167 model.ChildModels.Add (ReadCP (elem));
2170 else if(PeekChar () == ',') {
2172 model.OrderType = DTDContentOrderType.Seq;
2175 model.ChildModels.Add (ReadCP (elem));
2187 model = new DTDContentModel (DTD, elem.Name);
2188 model.ElementName = ReadName ();
2191 switch(PeekChar ()) {
2193 model.Occurence = DTDOccurence.Optional;
2197 model.Occurence = DTDOccurence.ZeroOrMore;
2201 model.Occurence = DTDOccurence.OneOrMore;
2208 // The reader is positioned on the first name char.
2209 private void ReadParameterEntityDecl ()
2211 DTDParameterEntityDeclaration decl =
2212 new DTDParameterEntityDeclaration();
2213 decl.BaseURI = BaseURI;
2215 decl.Name = ReadName ();
2218 if (PeekChar () == 'S' || PeekChar () == 'P') {
2219 // throw new NotImplementedException ("External parameter entity reference is not implemented yet.");
2220 // read publicId/systemId
2222 decl.PublicId = attributes ["PUBLIC"] as string;
2223 decl.SystemId = attributes ["SYSTEM"] as string;
2228 int quoteChar = ReadChar ();
2229 int start = currentTag.Length;
2232 int c = PeekChar ();
2234 throw new XmlException ("unexpected end of stream in entity value definition.");
2238 if (quoteChar == '"') goto SKIP;
2242 if (quoteChar == '\'') goto SKIP;
2253 decl.Value = currentTag.ToString (start, currentTag.Length - start - 1);
2257 if (parameterEntities [decl.Name] == null) {
2258 parameterEntities.Add (decl.Name, decl);
2262 // reader is positioned on '%'
2263 private void ImportAsPERef ()
2265 StringBuilder sb = null;
2266 int peRefStart = currentTag.Length;
2267 string appendStr = "";
2269 string peName = ReadName ();
2271 DTDParameterEntityDeclaration peDecl =
2272 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
2274 throw new XmlException (this as IXmlLineInfo,"Parameter entity " + peName + " not found.");
2275 if (peDecl.SystemId != null) {
2276 PushParserInput (peDecl.SystemId);
2278 sb = new StringBuilder ();
2281 while (PeekChar () != -1)
2282 sb.Append (ReadChar ());
2284 appendStr = sb.ToString ();
2286 appendStr = peDecl.Value;
2288 currentTag.Remove (peRefStart,
2289 currentTag.Length - peRefStart);
2290 currentTag.Append (Dereference (appendStr));
2293 // The reader is positioned on the head of the name.
2294 private DTDEntityDeclaration ReadEntityDecl ()
2296 DTDEntityDeclaration decl = new DTDEntityDeclaration ();
2297 decl.IsInternalSubset = isIntSubset;
2298 decl.Name = ReadName ();
2303 if (PeekChar () == 'S' || PeekChar () == 'P') {
2306 decl.PublicId = attributes ["PUBLIC"] as string;
2307 decl.SystemId = attributes ["SYSTEM"] as string;
2308 if (SkipWhitespace ()) {
2309 if (PeekChar () == 'N') {
2312 if (SkipWhitespace ())
2313 decl.NotationName = ReadName (); // ndata_name
2319 decl.EntityValue = ReadEntityValueDecl ();
2322 // This expanding is only allowed as a non-validating parser.
2328 private string ReadEntityValueDecl ()
2331 // quotation char will be finally removed on unescaping
2332 int quoteChar = ReadChar ();
2333 int start = currentTag.Length;
2334 if (quoteChar != '\'' && quoteChar != '"')
2335 throw new XmlException ("quotation char was expected.");
2337 while (PeekChar () != quoteChar) {
2338 switch (PeekChar ()) {
2340 this.ImportAsPERef ();
2344 ReadReference (true);
2347 throw new XmlException ("unexpected end of stream.");
2353 string value = Dereference (currentTag.ToString (start, currentTag.Length - start));
2358 private DTDAttListDeclaration ReadAttListDecl ()
2362 string name = ReadName (); // target element name
2363 DTDAttListDeclaration decl =
2364 DTD.AttListDecls [name] as DTDAttListDeclaration;
2366 decl = new DTDAttListDeclaration ();
2373 while (XmlConstructs.IsName ((char) PeekChar ())) {
2374 DTDAttributeDefinition def = ReadAttributeDefinition ();
2375 if (decl [def.Name] == null)
2382 // This expanding is only allowed as a non-validating parser.
2388 private DTDAttributeDefinition ReadAttributeDefinition ()
2390 DTDAttributeDefinition def = new DTDAttributeDefinition ();
2394 def.Name = ReadName ();
2399 switch(PeekChar ()) {
2402 def.Datatype = XmlSchemaDatatype.FromName ("normalizedString");
2404 case 'I': // ID, IDREF, IDREFS
2406 if(PeekChar () == 'R') {
2408 if(PeekChar () == 'S') {
2411 def.Datatype = XmlSchemaDatatype.FromName ("IDREFS");
2414 def.Datatype = XmlSchemaDatatype.FromName ("IDREF");
2417 def.Datatype = XmlSchemaDatatype.FromName ("ID");
2419 case 'E': // ENTITY, ENTITIES
2421 switch(ReadChar ()) {
2423 def.Datatype = XmlSchemaDatatype.FromName ("ENTITY");
2425 case 'I': // ENTITIES
2427 def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES");
2431 case 'N': // NMTOKEN, NMTOKENS, NOTATION
2433 switch(PeekChar ()) {
2436 if(PeekChar ()=='S') { // NMTOKENS
2438 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS");
2441 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN");
2445 def.Datatype = XmlSchemaDatatype.FromName ("NOTATION");
2449 def.EnumeratedNotations.Add (ReadName ()); // notation name
2451 while(PeekChar () == '|') {
2454 def.EnumeratedNotations.Add (ReadName ()); // notation name
2460 throw new XmlException ("attribute declaration syntax error.");
2463 default: // Enumerated Values
2467 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
2469 while(PeekChar () == '|') {
2472 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ()); // enum value
2483 if(PeekChar () == '#')
2489 Expect ("REQUIRED");
2490 def.OccurenceType = DTDAttributeOccurenceType.Required;
2494 def.OccurenceType = DTDAttributeOccurenceType.Optional;
2498 def.OccurenceType = DTDAttributeOccurenceType.Fixed;
2500 def.UnresolvedDefaultValue = ReadAttribute ();
2504 // one of the enumerated value
2505 if (PeekChar () == -1) {
2509 def.UnresolvedDefaultValue = ReadAttribute ();
2515 private DTDNotationDeclaration ReadNotationDecl()
2517 DTDNotationDeclaration decl = new DTDNotationDeclaration ();
2519 decl.Name = ReadName (); // notation name
2520 if (namespaces) { // copy from SetProperties ;-)
2521 int indexOfColon = decl.Name.IndexOf (':');
2523 if (indexOfColon == -1) {
2524 decl.Prefix = String.Empty;
2525 decl.LocalName = decl.Name;
2527 decl.Prefix = decl.Name.Substring (0, indexOfColon);
2528 decl.LocalName = decl.Name.Substring (indexOfColon + 1);
2531 decl.Prefix = String.Empty;
2532 decl.LocalName = decl.Name;
2536 if(PeekChar () == 'P') {
2537 decl.PublicId = ReadPubidLiteral ();
2539 if (PeekChar () == '\'' || PeekChar () == '"') {
2540 decl.SystemId = ReadSystemLiteral (false);
2543 } else if(PeekChar () == 'S') {
2544 decl.SystemId = ReadSystemLiteral (true);
2547 if(decl.PublicId == null && decl.SystemId == null)
2548 throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
2549 // This expanding is only allowed as a non-validating parser.
2555 private void TryExpandPERef ()
2557 if (PeekChar () == '%') {
2559 if (!XmlConstructs.IsName (PeekChar ()))
2565 // reader is positioned on the first letter of the name.
2566 private void ExpandPERef ()
2571 private void ExpandPERef (bool attachSpace)
2573 string peName = ReadName ();
2575 ExpandNamedPERef (peName, attachSpace);
2578 private void ExpandNamedPERef (string peName, bool attachSpace)
2580 DTDParameterEntityDeclaration decl =
2581 parameterEntities [peName] as DTDParameterEntityDeclaration;
2583 throw new XmlException ("undeclared parameter entity: '" + peName + "'");
2584 if (decl.SystemId != null) {
2585 PushParserInput (decl.SystemId);
2589 currentInput.InsertParameterEntityBuffer (attachSpace ? " " + Dereference (decl.Value) + " " : decl.Value);
2590 SkipWhitespace (); // is it ok?
2591 // while (PeekChar () == '%')
2592 // TryExpandPERef (); // recursive
2595 private void ReadExternalID() {
2596 switch(PeekChar ()) {
2598 attributes ["PUBLIC"] = null;
2599 attributes ["SYSTEM"] = ReadSystemLiteral (true);
2602 attributes ["PUBLIC"] = ReadPubidLiteral ();
2604 attributes ["SYSTEM"] = ReadSystemLiteral (false);
2609 // The reader is positioned on the first 'S' of "SYSTEM".
2610 private string ReadSystemLiteral (bool expectSYSTEM)
2615 int quoteChar = ReadChar (); // apos or quot
2616 int startPos = currentTag.Length;
2618 while(c != quoteChar) {
2620 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2622 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2625 private string ReadPubidLiteral()
2629 int quoteChar = ReadChar ();
2630 int startPos = currentTag.Length;
2632 while(c != quoteChar)
2635 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2636 if(c != quoteChar && !XmlConstructs.IsPubid (c))
2637 throw new XmlException (this as IXmlLineInfo,"character '" + (char)c + "' not allowed for PUBLIC ID");
2639 return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2642 // The reader is positioned on the first character
2644 internal string ReadName ()
2646 return ReadNameOrNmToken(false);
2649 // The reader is positioned on the first character
2651 private string ReadNmToken ()
2653 return ReadNameOrNmToken(true);
2656 private string ReadNameOrNmToken(bool isNameToken)
2658 int ch = PeekChar ();
2660 if (!XmlConstructs.IsName ((char) ch))
2661 throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char)ch));
2664 if (!XmlConstructs.IsNameStart ((char) ch))
2665 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
2670 AppendNameChar (ReadChar ());
2672 while (XmlConstructs.IsName (PeekChar ())) {
2673 AppendNameChar (ReadChar ());
2676 return CreateNameString ();
2679 // Read the next character and compare it against the
2680 // specified character.
2681 private void Expect (int expected)
2683 int ch = ReadChar ();
2685 if (ch != expected) {
2686 throw new XmlException (this as IXmlLineInfo,
2688 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2696 private void Expect (string expected)
2698 int len = expected.Length;
2699 for(int i=0; i< len; i++)
2700 Expect (expected[i]);
2703 // Does not consume the first non-whitespace character.
2704 private bool SkipWhitespace ()
2706 //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
2707 bool skipped = XmlConstructs.IsSpace (PeekChar ());
2708 while (XmlConstructs.IsSpace (PeekChar ()))
2713 private bool ReadWhitespace ()
2715 if (currentState == XmlNodeType.None)
2716 currentState = XmlNodeType.XmlDeclaration;
2718 ClearValueBuffer ();
2719 int ch = PeekChar ();
2721 AppendValueChar (ReadChar ());
2722 } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
2724 if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2727 SetProperties (XmlNodeType.Whitespace,
2733 return (PeekChar () != -1);
2736 // read entity reference from attribute string and if parsable then return the value.
2737 private string ReadAttributeValueReference ()
2739 int endEntityPosition = attributeString.IndexOf(';',
2741 if (endEntityPosition < 0)
2742 throw new XmlException ("Insufficient markup of entity reference");
2743 string entityName = attributeString.Substring (attributeValuePos + 1,
2744 endEntityPosition - attributeValuePos - 1);
2746 attributeValuePos = endEntityPosition + 1;
2748 if(entityName [0] == '#') {
2751 if(entityName [1] == 'x') {
2753 c = (char) int.Parse ("0" + entityName.Substring (2),
2754 System.Globalization.NumberStyles.HexNumber);
2757 c = (char) int.Parse (entityName.Substring (1));
2759 return c.ToString();
2764 case "lt": return "<";
2765 case "gt": return ">";
2766 case "amp": return "&";
2767 case "quot": return "\"";
2768 case "apos": return "'";
2769 default: return null;
2774 private string UnescapeAttributeValue (string unresolved)
2776 if(unresolved == null) return null;
2778 // trim start/end edge of quotation character.
2779 return Dereference (unresolved.Substring (1, unresolved.Length - 2));
2782 private string Dereference (string unresolved)
2784 StringBuilder resolved = new StringBuilder();
2786 int next = unresolved.IndexOf ('&');
2792 resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
2793 int endPos = unresolved.IndexOf (';', next+1);
2795 unresolved.Substring (next + 1, endPos - next - 1);
2796 if(entityName [0] == '#') {
2799 if(entityName [1] == 'x') {
2801 c = (char) int.Parse ("0" + entityName.Substring (2),
2802 System.Globalization.NumberStyles.HexNumber);
2805 c = (char) int.Parse (entityName.Substring (1));
2807 resolved.Append (c);
2809 switch(entityName) {
2810 case "lt": resolved.Append ("<"); break;
2811 case "gt": resolved.Append (">"); break;
2812 case "amp": resolved.Append ("&"); break;
2813 case "quot": resolved.Append ("\""); break;
2814 case "apos": resolved.Append ("'"); break;
2815 // With respect to "Value", MS document is helpless
2816 // and the implemention returns inconsistent value
2817 // (e.g. XML: "&ent; &ent;" ---> Value: "&ent; &ent;".)
2818 default: resolved.Append ("&" + entityName + ";"); break;
2822 if(pos > unresolved.Length)
2824 next = unresolved.IndexOf('&', pos);
2826 resolved.Append (unresolved.Substring(pos));
2828 return resolved.ToString();