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/
10 // Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //#define USE_NAME_BUFFER
34 // Optimization TODOs:
36 // - support PushbackChar() which reverts one character read.
37 // - ReadTextReader() should always keep one pushback buffer
38 // as pushback safety net.
39 // - Replace (peek,read) * n -> read * n + pushback
43 using System
.Collections
;
45 using System
.Collections
.Generic
;
47 using System
.Globalization
;
49 using System
.Security
.Permissions
;
51 using System
.Xml
.Schema
;
64 internal class XmlTextReader
: XmlReader
,
65 IXmlLineInfo
, IXmlNamespaceResolver
, IHasXmlParserContext
67 [PermissionSet (SecurityAction
.InheritanceDemand
, Unrestricted
= true)]
68 public class XmlTextReader
: XmlReader
, IXmlLineInfo
, IHasXmlParserContext
73 protected XmlTextReader ()
77 public XmlTextReader (Stream input
)
78 : this (new XmlStreamReader (input
))
82 public XmlTextReader (string url
)
83 : this(url
, new NameTable ())
87 public XmlTextReader (TextReader input
)
88 : this (input
, new NameTable ())
92 protected XmlTextReader (XmlNameTable nt
)
93 : this (String
.Empty
, null, XmlNodeType
.None
, null)
97 public XmlTextReader (Stream input
, XmlNameTable nt
)
98 : this(new XmlStreamReader (input
), nt
)
102 public XmlTextReader (string url
, Stream input
)
103 : this (url
, new XmlStreamReader (input
))
107 public XmlTextReader (string url
, TextReader input
)
108 : this (url
, input
, new NameTable ())
112 public XmlTextReader (string url
, XmlNameTable nt
)
115 Stream stream
= GetStreamFromUrl (url
, out uriString
);
116 XmlParserContext ctx
= new XmlParserContext (nt
,
117 new XmlNamespaceManager (nt
),
120 this.InitializeContext (uriString
, ctx
, new XmlStreamReader (stream
), XmlNodeType
.Document
);
123 public XmlTextReader (TextReader input
, XmlNameTable nt
)
124 : this (String
.Empty
, input
, nt
)
128 // This is used in XmlReader.Create() to indicate that string
129 // argument is uri, not an xml fragment.
130 internal XmlTextReader (bool dummy
, XmlResolver resolver
, string url
, XmlNodeType fragType
, XmlParserContext context
)
132 if (resolver
== null) {
134 resolver
= new XmlXapResolver ();
136 resolver
= new XmlUrlResolver ();
139 this.XmlResolver
= resolver
;
141 Stream stream
= GetStreamFromUrl (url
, out uriString
);
142 this.InitializeContext (uriString
, context
, new XmlStreamReader (stream
), fragType
);
145 public XmlTextReader (Stream xmlFragment
, XmlNodeType fragType
, XmlParserContext context
)
146 : this (context
!= null ? context
.BaseURI
: String
.Empty
,
147 new XmlStreamReader (xmlFragment
),
151 disallowReset
= true;
154 internal XmlTextReader (string baseURI
, TextReader xmlFragment
, XmlNodeType fragType
)
155 : this (baseURI
, xmlFragment
, fragType
, null)
159 public XmlTextReader (string url
, Stream input
, XmlNameTable nt
)
160 : this (url
, new XmlStreamReader (input
), nt
)
164 public XmlTextReader (string url
, TextReader input
, XmlNameTable nt
)
165 : this (url
, input
, XmlNodeType
.Document
, null)
169 public XmlTextReader (string xmlFragment
, XmlNodeType fragType
, XmlParserContext context
)
170 : this (context
!= null ? context
.BaseURI
: String
.Empty
,
171 new StringReader (xmlFragment
),
175 disallowReset
= true;
178 internal XmlTextReader (string url
, TextReader fragment
, XmlNodeType fragType
, XmlParserContext context
)
180 InitializeContext (url
, context
, fragment
, fragType
);
183 private Stream
GetStreamFromUrl (string url
, out string absoluteUriString
)
187 throw new ArgumentNullException ("url");
189 throw new ArgumentException ("url");
191 Uri uri
= resolver
.ResolveUri (null, url
);
192 absoluteUriString
= uri
!= null ? uri
.ToString () : String
.Empty
;
193 return resolver
.GetEntity (uri
, null, typeof (Stream
)) as Stream
;
200 public override int AttributeCount
202 get { return attributeCount; }
205 public override string BaseURI
207 get { return parserContext.BaseURI; }
211 public override bool CanReadBinaryContent
{
215 public override bool CanReadValueChunk
{
219 internal override bool CanReadBinaryContent
{
223 internal override bool CanReadValueChunk
{
228 internal bool CharacterChecking
{
229 get { return checkCharacters; }
230 set { checkCharacters = value; }
233 // for XmlReaderSettings.CloseInput support
234 internal bool CloseInput
{
235 get { return closeInput; }
236 set { closeInput = value; }
239 public override int Depth
242 int nodeTypeMod
= currentToken
.NodeType
== XmlNodeType
.Element
? 0 : -1;
243 if (currentAttributeValue
>= 0)
244 return nodeTypeMod
+ elementDepth
+ 2; // inside attribute value.
245 else if (currentAttribute
>= 0)
246 return nodeTypeMod
+ elementDepth
+ 1;
251 public Encoding Encoding
253 get { return parserContext.Encoding; }
256 public EntityHandling EntityHandling
{
257 get { return entityHandling; }
258 set { entityHandling = value; }
262 public override bool EOF
{
263 get { return readState == ReadState.EndOfFile; }
266 public override bool HasValue
{
267 get { return cursorToken.Value != null; }
270 public override bool IsDefault
{
271 // XmlTextReader does not expand default attributes.
272 get { return false; }
275 public override bool IsEmptyElement
{
276 get { return cursorToken.IsEmptyElement; }
281 public override string this [int i
] {
282 get { return GetAttribute (i); }
285 public override string this [string name
] {
286 get { return GetAttribute (name); }
289 public override string this [string localName
, string namespaceName
] {
290 get { return GetAttribute (localName, namespaceName); }
294 public int LineNumber
{
296 if (useProceedingLineInfo
)
299 return cursorToken
.LineNumber
;
303 public int LinePosition
{
305 if (useProceedingLineInfo
)
308 return cursorToken
.LinePosition
;
312 public override string LocalName
{
313 get { return cursorToken.LocalName; }
316 public override string Name
{
317 get { return cursorToken.Name; }
320 public bool Namespaces
{
321 get { return namespaces; }
323 if (readState
!= ReadState
.Initial
)
324 throw new InvalidOperationException ("Namespaces have to be set before reading.");
329 public override string NamespaceURI
{
330 get { return cursorToken.NamespaceURI; }
333 public override XmlNameTable NameTable
{
334 get { return nameTable; }
337 public override XmlNodeType NodeType
{
338 get { return cursorToken.NodeType; }
341 public bool Normalization
{
342 get { return normalization; }
343 set { normalization = value; }
346 public override string Prefix
{
347 get { return cursorToken.Prefix; }
350 public bool ProhibitDtd
{
351 get { return prohibitDtd; }
352 set { prohibitDtd = value; }
355 public override char QuoteChar
{
356 get { return cursorToken.QuoteChar; }
359 public override ReadState ReadState
{
360 get { return readState; }
364 public override XmlReaderSettings Settings
{
365 get { return base.Settings; }
369 public override string Value
{
370 get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
373 public WhitespaceHandling WhitespaceHandling
{
374 get { return whitespaceHandling; }
375 set { whitespaceHandling = value; }
378 public override string XmlLang
{
379 get { return parserContext.XmlLang; }
382 public XmlResolver XmlResolver
{
383 set { resolver = value; }
386 public override XmlSpace XmlSpace
{
387 get { return parserContext.XmlSpace; }
394 public override void Close ()
396 readState
= ReadState
.Closed
;
398 cursorToken
.Clear ();
399 currentToken
.Clear ();
401 if (closeInput
&& reader
!= null)
405 public override string GetAttribute (int i
)
407 if (i
>= attributeCount
)
408 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
410 return attributeTokens
[i
].Value
;
414 // MS.NET 1.0 msdn says that this method returns String.Empty
415 // for absent attribute, but in fact it returns null.
416 // This description is corrected in MS.NET 1.1 msdn.
417 public override string GetAttribute (string name
)
419 for (int i
= 0; i
< attributeCount
; i
++)
420 if (attributeTokens
[i
].Name
== name
)
421 return attributeTokens
[i
].Value
;
425 private int GetIndexOfQualifiedAttribute (string localName
, string namespaceURI
)
427 for (int i
= 0; i
< attributeCount
; i
++) {
428 XmlAttributeTokenInfo ti
= attributeTokens
[i
];
429 if (ti
.LocalName
== localName
&& ti
.NamespaceURI
== namespaceURI
)
435 XmlParserContext IHasXmlParserContext
.ParserContext
{
436 get { return parserContext; }
439 public override string GetAttribute (string localName
, string namespaceURI
)
441 int idx
= this.GetIndexOfQualifiedAttribute (localName
, namespaceURI
);
444 return attributeTokens
[idx
].Value
;
448 public IDictionary
<string, string> GetNamespacesInScope (XmlNamespaceScope scope
)
450 return nsmgr
.GetNamespacesInScope (scope
);
453 IDictionary
<string, string> IXmlNamespaceResolver
.GetNamespacesInScope (XmlNamespaceScope scope
)
455 return GetNamespacesInScope (scope
);
459 public TextReader
GetRemainder ()
461 if (peekCharsLength
< 0)
463 return new StringReader (new string (peekChars
, peekCharsIndex
, peekCharsLength
- peekCharsIndex
) + reader
.ReadToEnd ());
467 public bool HasLineInfo ()
469 bool IXmlLineInfo
.HasLineInfo ()
475 public override string LookupNamespace (string prefix
)
477 return LookupNamespace (prefix
, false);
480 private string LookupNamespace (string prefix
, bool atomizedNames
)
482 string s
= nsmgr
.LookupNamespace (
483 prefix
, atomizedNames
);
484 return s
== String
.Empty
? null : s
;
488 string IXmlNamespaceResolver
.LookupPrefix (string ns
)
490 return LookupPrefix (ns
, false);
493 public string LookupPrefix (string ns
, bool atomizedName
)
495 return nsmgr
.LookupPrefix (ns
, atomizedName
);
499 public override void MoveToAttribute (int i
)
501 if (i
>= attributeCount
)
502 throw new ArgumentOutOfRangeException ("attribute index out of range.");
504 currentAttribute
= i
;
505 currentAttributeValue
= -1;
506 cursorToken
= attributeTokens
[i
];
509 public override bool MoveToAttribute (string name
)
511 for (int i
= 0; i
< attributeCount
; i
++) {
512 XmlAttributeTokenInfo ti
= attributeTokens
[i
];
513 if (ti
.Name
== name
) {
521 public override bool MoveToAttribute (string localName
, string namespaceName
)
523 int idx
= GetIndexOfQualifiedAttribute (localName
, namespaceName
);
526 MoveToAttribute (idx
);
530 public override bool MoveToElement ()
532 if (currentToken
== null) // for attribute .ctor()
535 if (cursorToken
== currentToken
)
538 if (currentAttribute
>= 0) {
539 currentAttribute
= -1;
540 currentAttributeValue
= -1;
541 cursorToken
= currentToken
;
548 public override bool MoveToFirstAttribute ()
550 if (attributeCount
== 0)
553 return MoveToNextAttribute ();
556 public override bool MoveToNextAttribute ()
558 if (currentAttribute
== 0 && attributeCount
== 0)
560 if (currentAttribute
+ 1 < attributeCount
) {
562 currentAttributeValue
= -1;
563 cursorToken
= attributeTokens
[currentAttribute
];
570 public override bool Read ()
572 if (readState
== ReadState
.Closed
)
574 curNodePeekIndex
= peekCharsIndex
;
575 preserveCurrentTag
= true;
579 if (startNodeType
== XmlNodeType
.Attribute
) {
580 if (currentAttribute
== 0)
581 return false; // already read.
582 SkipTextDeclaration ();
584 IncrementAttributeToken ();
585 ReadAttributeValueTokens ('"');
586 cursorToken
= attributeTokens
[0];
587 currentAttributeValue
= -1;
588 readState
= ReadState
.Interactive
;
591 if (readState
== ReadState
.Initial
&& currentState
== XmlNodeType
.Element
)
592 SkipTextDeclaration ();
598 readState
= ReadState
.Interactive
;
599 currentLinkedNodeLineNumber
= line
;
600 currentLinkedNodeLinePosition
= column
;
601 useProceedingLineInfo
= true;
603 cursorToken
= currentToken
;
605 currentAttribute
= currentAttributeValue
= -1;
606 currentToken
.Clear ();
608 // It was moved from end of ReadStartTag ().
614 if (readCharsInProgress
) {
615 readCharsInProgress
= false;
616 return ReadUntilEndTag ();
619 more
= ReadContent ();
621 if (!more
&& startNodeType
== XmlNodeType
.Document
&& currentState
!= XmlNodeType
.EndElement
)
622 throw NotWFError ("Document element did not appear.");
624 useProceedingLineInfo
= false;
628 public override bool ReadAttributeValue ()
630 if (readState
== ReadState
.Initial
&& startNodeType
== XmlNodeType
.Attribute
) {
634 if (currentAttribute
< 0)
636 XmlAttributeTokenInfo ti
= attributeTokens
[currentAttribute
];
637 if (currentAttributeValue
< 0)
638 currentAttributeValue
= ti
.ValueTokenStartIndex
- 1;
640 if (currentAttributeValue
< ti
.ValueTokenEndIndex
) {
641 currentAttributeValue
++;
642 cursorToken
= attributeValueTokens
[currentAttributeValue
];
649 public int ReadBase64 (byte [] buffer
, int offset
, int length
)
651 BinaryCharGetter
= binaryCharGetter
;
653 return Binary
.ReadBase64 (buffer
, offset
, length
);
655 BinaryCharGetter
= null;
659 public int ReadBinHex (byte [] buffer
, int offset
, int length
)
661 BinaryCharGetter
= binaryCharGetter
;
663 return Binary
.ReadBinHex (buffer
, offset
, length
);
665 BinaryCharGetter
= null;
669 public int ReadChars (char [] buffer
, int offset
, int length
)
672 throw new ArgumentOutOfRangeException (
676 "Offset must be non-negative integer.");
678 } else if (length
< 0) {
679 throw new ArgumentOutOfRangeException (
683 "Length must be non-negative integer.");
685 } else if (buffer
.Length
< offset
+ length
)
686 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
688 if (IsEmptyElement
) {
693 if (!readCharsInProgress
&& NodeType
!= XmlNodeType
.Element
)
696 preserveCurrentTag
= false;
697 readCharsInProgress
= true;
698 useProceedingLineInfo
= true;
700 return ReadCharsInternal (buffer
, offset
, length
);
703 public void ResetState ()
706 throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment.");
710 public override void ResolveEntity ()
712 // XmlTextReader does not resolve entities.
713 throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
717 [MonoTODO
] // FIXME: Implement, for performance improvement
718 public override void Skip ()
726 // Parsed DTD Objects
727 // Note that thgis property must be kept since dtd2xsd uses it.
728 internal DTDObjectModel DTD
{
729 get { return parserContext.Dtd; }
732 internal XmlResolver Resolver
{
733 get { return resolver; }
738 internal class XmlTokenInfo
740 public XmlTokenInfo (XmlTextReader xtr
)
748 protected XmlTextReader Reader
;
751 public string LocalName
;
752 public string Prefix
;
753 public string NamespaceURI
;
754 public bool IsEmptyElement
;
755 public char QuoteChar
;
756 public int LineNumber
;
757 public int LinePosition
;
758 public int ValueBufferStart
;
759 public int ValueBufferEnd
;
761 public XmlNodeType NodeType
;
763 public virtual string Value
{
765 if (valueCache
!= null)
767 if (ValueBufferStart
>= 0) {
768 //Console.WriteLine (NodeType + " / " + ValueBuffer.Length + " / " + ValueBufferStart + " / " + ValueBufferEnd);
769 valueCache
= Reader
.valueBuffer
.ToString (ValueBufferStart
, ValueBufferEnd
- ValueBufferStart
);
773 case XmlNodeType
.Text
:
774 case XmlNodeType
.SignificantWhitespace
:
775 case XmlNodeType
.Whitespace
:
776 case XmlNodeType
.Comment
:
777 case XmlNodeType
.CDATA
:
778 case XmlNodeType
.ProcessingInstruction
:
779 valueCache
= Reader
.CreateValueString ();
784 set { valueCache = value; }
787 public virtual void Clear ()
789 ValueBufferStart
= -1;
791 NodeType
= XmlNodeType
.None
;
792 Name
= LocalName
= Prefix
= NamespaceURI
= String
.Empty
;
793 IsEmptyElement
= false;
795 LineNumber
= LinePosition
= 0;
799 internal class XmlAttributeTokenInfo
: XmlTokenInfo
801 public XmlAttributeTokenInfo (XmlTextReader reader
)
804 NodeType
= XmlNodeType
.Attribute
;
807 public int ValueTokenStartIndex
;
808 public int ValueTokenEndIndex
;
810 StringBuilder tmpBuilder
= new StringBuilder ();
812 public override string Value
{
814 if (valueCache
!= null)
817 // An empty value should return String.Empty.
818 if (ValueTokenStartIndex
== ValueTokenEndIndex
) {
819 XmlTokenInfo ti
= Reader
.attributeValueTokens
[ValueTokenStartIndex
];
820 if (ti
.NodeType
== XmlNodeType
.EntityReference
)
821 valueCache
= String
.Concat ("&", ti
.Name
, ";");
823 valueCache
= ti
.Value
;
827 tmpBuilder
.Length
= 0;
828 for (int i
= ValueTokenStartIndex
; i
<= ValueTokenEndIndex
; i
++) {
829 XmlTokenInfo ti
= Reader
.attributeValueTokens
[i
];
830 if (ti
.NodeType
== XmlNodeType
.Text
)
831 tmpBuilder
.Append (ti
.Value
);
833 tmpBuilder
.Append ('&');
834 tmpBuilder
.Append (ti
.Name
);
835 tmpBuilder
.Append (';');
839 valueCache
= tmpBuilder
.ToString (0, tmpBuilder
.Length
);
843 set { valueCache = value; }
846 public override void Clear ()
850 NodeType
= XmlNodeType
.Attribute
;
851 ValueTokenStartIndex
= ValueTokenEndIndex
= 0;
854 internal void FillXmlns ()
856 if (Object
.ReferenceEquals (Prefix
, XmlNamespaceManager
.PrefixXmlns
))
857 Reader
.nsmgr
.AddNamespace (LocalName
, Value
);
858 else if (Object
.ReferenceEquals (Name
, XmlNamespaceManager
.PrefixXmlns
))
859 Reader
.nsmgr
.AddNamespace (String
.Empty
, Value
);
862 internal void FillNamespace ()
864 if (Object
.ReferenceEquals (Prefix
, XmlNamespaceManager
.PrefixXmlns
) ||
865 Object
.ReferenceEquals (Name
, XmlNamespaceManager
.PrefixXmlns
))
866 NamespaceURI
= XmlNamespaceManager
.XmlnsXmlns
;
867 else if (Prefix
.Length
== 0)
868 NamespaceURI
= string.Empty
;
870 NamespaceURI
= Reader
.LookupNamespace (Prefix
, true);
874 private XmlTokenInfo cursorToken
;
875 private XmlTokenInfo currentToken
;
876 private XmlAttributeTokenInfo currentAttributeToken
;
877 private XmlTokenInfo currentAttributeValueToken
;
878 private XmlAttributeTokenInfo
[] attributeTokens
= new XmlAttributeTokenInfo
[10];
879 private XmlTokenInfo
[] attributeValueTokens
= new XmlTokenInfo
[10];
880 private int currentAttribute
;
881 private int currentAttributeValue
;
882 private int attributeCount
;
884 private XmlParserContext parserContext
;
885 private XmlNameTable nameTable
;
886 private XmlNamespaceManager nsmgr
;
888 private ReadState readState
;
889 private bool disallowReset
;
892 private int elementDepth
;
893 private bool depthUp
;
895 private bool popScope
;
899 public TagName (string n
, string l
, string p
)
906 public readonly string Name
;
907 public readonly string LocalName
;
908 public readonly string Prefix
;
911 private TagName
[] elementNames
;
912 int elementNameStackPos
;
914 private bool allowMultipleRoot
;
916 private bool isStandalone
;
918 private bool returnEntityReference
;
919 private string entityReferenceName
;
922 private char [] nameBuffer
;
923 private int nameLength
;
924 private int nameCapacity
;
925 private const int initialNameCapacity
= 32;
928 private StringBuilder valueBuffer
;
930 private TextReader reader
;
931 private char [] peekChars
;
932 private int peekCharsIndex
;
933 private int peekCharsLength
;
934 private int curNodePeekIndex
;
935 private bool preserveCurrentTag
;
936 private const int peekCharCapacity
= 1024;
941 private int currentLinkedNodeLineNumber
;
942 private int currentLinkedNodeLinePosition
;
943 private bool useProceedingLineInfo
;
945 private XmlNodeType startNodeType
;
946 // State machine attribute.
947 // XmlDeclaration: after the first node.
948 // DocumentType: after doctypedecl
949 // Element: inside document element
950 // EndElement: after document element
951 private XmlNodeType currentState
;
953 // For ReadChars()/ReadBase64()/ReadBinHex()
954 private int nestLevel
;
955 private bool readCharsInProgress
;
956 XmlReaderBinarySupport
.CharGetter binaryCharGetter
;
958 // These values are never re-initialized.
959 private bool namespaces
= true;
960 private WhitespaceHandling whitespaceHandling
= WhitespaceHandling
.All
;
962 private XmlResolver resolver
= new XmlXapResolver ();
964 private XmlResolver resolver
= new XmlUrlResolver ();
966 private bool normalization
= false;
968 private bool checkCharacters
;
969 private bool prohibitDtd
= false;
970 private bool closeInput
= true;
971 private EntityHandling entityHandling
; // 2.0
973 private NameTable whitespacePool
;
974 private char [] whitespaceCache
;
976 private XmlException
NotWFError (string message
)
978 return new XmlException (this as IXmlLineInfo
, BaseURI
, message
);
983 allowMultipleRoot
= false;
984 elementNames
= new TagName
[10];
985 valueBuffer
= new StringBuilder ();
986 binaryCharGetter
= new XmlReaderBinarySupport
.CharGetter (ReadChars
);
988 nameBuffer
= new char [initialNameCapacity
];
991 checkCharacters
= true;
993 if (Settings
!= null)
994 checkCharacters
= Settings
.CheckCharacters
;
998 entityHandling
= EntityHandling
.ExpandCharEntities
;
1001 if (peekChars
== null)
1002 peekChars
= new char [peekCharCapacity
];
1003 peekCharsLength
= -1;
1004 curNodePeekIndex
= -1; // read from start
1009 currentLinkedNodeLineNumber
= currentLinkedNodeLinePosition
= 0;
1014 private void Clear ()
1016 currentToken
= new XmlTokenInfo (this);
1017 cursorToken
= currentToken
;
1018 currentAttribute
= -1;
1019 currentAttributeValue
= -1;
1022 readState
= ReadState
.Initial
;
1028 popScope
= allowMultipleRoot
= false;
1029 elementNameStackPos
= 0;
1031 isStandalone
= false;
1032 returnEntityReference
= false;
1033 entityReferenceName
= String
.Empty
;
1037 nameCapacity
= initialNameCapacity
;
1039 useProceedingLineInfo
= false;
1041 currentState
= XmlNodeType
.None
;
1043 readCharsInProgress
= false;
1046 private void InitializeContext (string url
, XmlParserContext context
, TextReader fragment
, XmlNodeType fragType
)
1048 startNodeType
= fragType
;
1049 parserContext
= context
;
1050 if (context
== null) {
1051 XmlNameTable nt
= new NameTable ();
1052 parserContext
= new XmlParserContext (nt
,
1053 new XmlNamespaceManager (nt
),
1057 nameTable
= parserContext
.NameTable
;
1058 nameTable
= nameTable
!= null ? nameTable
: new NameTable ();
1059 nsmgr
= parserContext
.NamespaceManager
;
1060 nsmgr
= nsmgr
!= null ? nsmgr
: new XmlNamespaceManager (nameTable
);
1062 if (url
!= null && url
.Length
> 0) {
1064 Uri uri
= new Uri (url
, UriKind
.RelativeOrAbsolute
);
1069 uri
= new Uri (url
, UriKind
.RelativeOrAbsolute
);
1071 uri
= new Uri (url
);
1073 } catch (Exception
) {
1074 string path
= Path
.GetFullPath ("./a");
1075 uri
= new Uri (new Uri (path
), url
);
1078 parserContext
.BaseURI
= uri
.ToString ();
1086 case XmlNodeType
.Attribute
:
1087 reader
= new StringReader (fragment
.ReadToEnd ().Replace ("\"", """));
1089 case XmlNodeType
.Element
:
1090 currentState
= XmlNodeType
.Element
;
1091 allowMultipleRoot
= true;
1093 case XmlNodeType
.Document
:
1096 throw new XmlException (String
.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType
));
1101 internal ConformanceLevel Conformance
{
1102 get { return allowMultipleRoot ? ConformanceLevel.Fragment : ConformanceLevel.Document; }
1104 if (value == ConformanceLevel
.Fragment
) {
1105 currentState
= XmlNodeType
.Element
;
1106 allowMultipleRoot
= true;
1111 internal void AdjustLineInfoOffset (int lineNumberOffset
, int linePositionOffset
)
1113 line
+= lineNumberOffset
;
1114 column
+= linePositionOffset
;
1117 internal void SetNameTable (XmlNameTable nameTable
)
1119 parserContext
.NameTable
= nameTable
;
1123 // Use this method rather than setting the properties
1124 // directly so that all the necessary properties can
1125 // be changed in harmony with each other. Maybe the
1126 // fields should be in a seperate class to help enforce
1129 // Namespace URI could not be provided here.
1130 private void SetProperties (
1131 XmlNodeType nodeType
,
1135 bool isEmptyElement
,
1137 bool clearAttributes
)
1139 SetTokenProperties (currentToken
, nodeType
, name
, prefix
, localName
, isEmptyElement
, value, clearAttributes
);
1140 currentToken
.LineNumber
= this.currentLinkedNodeLineNumber
;
1141 currentToken
.LinePosition
= this.currentLinkedNodeLinePosition
;
1144 private void SetTokenProperties (
1146 XmlNodeType nodeType
,
1150 bool isEmptyElement
,
1152 bool clearAttributes
)
1154 token
.NodeType
= nodeType
;
1156 token
.Prefix
= prefix
;
1157 token
.LocalName
= localName
;
1158 token
.IsEmptyElement
= isEmptyElement
;
1159 token
.Value
= value;
1160 this.elementDepth
= depth
;
1162 if (clearAttributes
)
1166 private void ClearAttributes ()
1168 //for (int i = 0; i < attributeCount; i++)
1169 // attributeTokens [i].Clear ();
1171 currentAttribute
= -1;
1172 currentAttributeValue
= -1;
1175 private int PeekSurrogate (int c
)
1177 if (peekCharsLength
<= peekCharsIndex
+ 1) {
1178 if (!ReadTextReader (c
))
1179 //FIXME: copy MS.NET behaviour when unpaired surrogate found
1183 int highhalfChar
= peekChars
[peekCharsIndex
];
1184 int lowhalfChar
= peekChars
[peekCharsIndex
+1];
1186 if (((highhalfChar
& 0xFC00) != 0xD800) || ((lowhalfChar
& 0xFC00) != 0xDC00))
1187 //FIXME: copy MS.NET behaviour when unpaired surrogate found
1188 return highhalfChar
;
1189 return 0x10000 + (highhalfChar
-0xD800)*0x400 + (lowhalfChar
-0xDC00);
1192 private int PeekChar ()
1194 if (peekCharsIndex
< peekCharsLength
) {
1195 int c
= peekChars
[peekCharsIndex
];
1198 if (c
< 0xD800 || c
>= 0xDFFF)
1200 return PeekSurrogate (c
);
1202 if (!ReadTextReader (-1))
1208 private int ReadChar ()
1210 int ch
= PeekChar ();
1214 peekCharsIndex
++; //Increment by 2 when a compound UCS-4 character was found
1219 } else if (ch
!= -1) {
1225 private void Advance (int ch
) {
1229 peekCharsIndex
++; //Increment by 2 when a compound UCS-4 character was found
1234 } else if (ch
!= -1) {
1239 private bool ReadTextReader (int remained
)
1241 if (peekCharsLength
< 0) { // initialized buffer
1242 peekCharsLength
= reader
.Read (peekChars
, 0, peekChars
.Length
);
1243 return peekCharsLength
> 0;
1245 int offset
= remained
>= 0 ? 1 : 0;
1246 int copysize
= peekCharsLength
- curNodePeekIndex
;
1248 // It must assure that current tag content always exists
1250 if (!preserveCurrentTag
) {
1251 curNodePeekIndex
= 0;
1254 } else if (peekCharsLength
< peekChars
.Length
) {
1255 // NonBlockingStreamReader returned less bytes
1256 // than the size of the buffer. In that case,
1257 // just refill the buffer.
1258 } else if (curNodePeekIndex
<= (peekCharsLength
>> 1)) {
1259 // extend the buffer
1260 char [] tmp
= new char [peekChars
.Length
* 2];
1261 Array
.Copy (peekChars
, curNodePeekIndex
,
1264 curNodePeekIndex
= 0;
1265 peekCharsIndex
= copysize
;
1267 Array
.Copy (peekChars
, curNodePeekIndex
,
1268 peekChars
, 0, copysize
);
1269 curNodePeekIndex
= 0;
1270 peekCharsIndex
= copysize
;
1273 peekChars
[peekCharsIndex
] = (char) remained
;
1274 int count
= peekChars
.Length
- peekCharsIndex
- offset
;
1275 if (count
> peekCharCapacity
)
1276 count
= peekCharCapacity
;
1277 int read
= reader
.Read (
1278 peekChars
, peekCharsIndex
+ offset
, count
);
1279 int remainingSize
= offset
+ read
;
1280 peekCharsLength
= peekCharsIndex
+ remainingSize
;
1282 return (remainingSize
!= 0);
1285 private bool ReadContent ()
1289 parserContext
.PopScope ();
1293 if (returnEntityReference
)
1294 SetEntityReferenceProperties ();
1296 int c
= PeekChar ();
1298 readState
= ReadState
.EndOfFile
;
1299 ClearValueBuffer ();
1301 XmlNodeType
.None
, // nodeType
1302 String
.Empty
, // name
1303 String
.Empty
, // prefix
1304 String
.Empty
, // localName
1305 false, // isEmptyElement
1307 true // clearAttributes
1310 throw NotWFError ("unexpected end of file. Current depth is " + depth
);
1317 switch (PeekChar ())
1325 ReadProcessingInstruction ();
1340 if (!ReadWhitespace ())
1342 return ReadContent ();
1350 return this.ReadState
!= ReadState
.EndOfFile
;
1353 private void SetEntityReferenceProperties ()
1355 DTDEntityDeclaration decl
= DTD
!= null ? DTD
.EntityDecls
[entityReferenceName
] : null;
1356 if (this.isStandalone
)
1357 if (DTD
== null || decl
== null || !decl
.IsInternalSubset
)
1358 throw NotWFError ("Standalone document must not contain any references to an non-internally declared entity.");
1359 if (decl
!= null && decl
.NotationName
!= null)
1360 throw NotWFError ("Reference to any unparsed entities is not allowed here.");
1362 ClearValueBuffer ();
1364 XmlNodeType
.EntityReference
, // nodeType
1365 entityReferenceName
, // name
1366 String
.Empty
, // prefix
1367 entityReferenceName
, // localName
1368 false, // isEmptyElement
1370 true // clearAttributes
1373 returnEntityReference
= false;
1374 entityReferenceName
= String
.Empty
;
1377 // The leading '<' has already been consumed.
1378 private void ReadStartTag ()
1380 if (currentState
== XmlNodeType
.EndElement
)
1381 throw NotWFError ("Multiple document element was detected.");
1382 currentState
= XmlNodeType
.Element
;
1386 currentLinkedNodeLineNumber
= line
;
1387 currentLinkedNodeLinePosition
= column
;
1389 string prefix
, localName
;
1390 string name
= ReadName (out prefix
, out localName
);
1391 if (currentState
== XmlNodeType
.EndElement
)
1392 throw NotWFError ("document has terminated, cannot open new element");
1394 bool isEmptyElement
= false;
1399 if (XmlChar
.IsFirstNameChar (PeekChar ()))
1400 ReadAttributes (false);
1401 cursorToken
= this.currentToken
;
1404 for (int i
= 0; i
< attributeCount
; i
++)
1405 attributeTokens
[i
].FillXmlns ();
1406 for (int i
= 0; i
< attributeCount
; i
++)
1407 attributeTokens
[i
].FillNamespace ();
1411 for (int i
= 0; i
< attributeCount
; i
++)
1412 if (attributeTokens
[i
].Prefix
== "xmlns" &&
1413 attributeTokens
[i
].Value
== String
.Empty
)
1414 throw NotWFError ("Empty namespace URI cannot be mapped to non-empty prefix.");
1416 for (int i
= 0; i
< attributeCount
; i
++) {
1417 for (int j
= i
+ 1; j
< attributeCount
; j
++)
1418 if (Object
.ReferenceEquals (attributeTokens
[i
].Name
, attributeTokens
[j
].Name
) ||
1419 (Object
.ReferenceEquals (attributeTokens
[i
].LocalName
, attributeTokens
[j
].LocalName
) &&
1420 Object
.ReferenceEquals (attributeTokens
[i
].NamespaceURI
, attributeTokens
[j
].NamespaceURI
)))
1421 throw NotWFError ("Attribute name and qualified name must be identical.");
1424 if (PeekChar () == '/') {
1426 isEmptyElement
= true;
1431 PushElementName (name
, localName
, prefix
);
1433 parserContext
.PushScope ();
1438 XmlNodeType
.Element
, // nodeType
1442 isEmptyElement
, // isEmptyElement
1444 false // clearAttributes
1446 if (prefix
.Length
> 0)
1447 currentToken
.NamespaceURI
= LookupNamespace (prefix
, true);
1448 else if (namespaces
)
1449 currentToken
.NamespaceURI
= nsmgr
.DefaultNamespace
;
1452 if (NamespaceURI
== null)
1453 throw NotWFError (String
.Format ("'{0}' is undeclared namespace.", Prefix
));
1455 for (int i
= 0; i
< attributeCount
; i
++) {
1456 MoveToAttribute (i
);
1457 if (NamespaceURI
== null)
1458 throw NotWFError (String
.Format ("'{0}' is undeclared namespace.", Prefix
));
1465 for (int i
= 0; i
< attributeCount
; i
++) {
1466 if (!Object
.ReferenceEquals (attributeTokens
[i
].Prefix
, XmlNamespaceManager
.PrefixXml
))
1468 string aname
= attributeTokens
[i
].LocalName
;
1469 string value = attributeTokens
[i
].Value
;
1472 if (this.resolver
!= null) {
1474 BaseURI
!= String
.Empty
?
1475 new Uri (BaseURI
) : null;
1476 Uri uri
= resolver
.ResolveUri (
1478 parserContext
.BaseURI
=
1484 parserContext
.BaseURI
= value;
1487 parserContext
.XmlLang
= value;
1492 parserContext
.XmlSpace
= XmlSpace
.Preserve
;
1495 parserContext
.XmlSpace
= XmlSpace
.Default
;
1498 throw NotWFError (String
.Format ("Invalid xml:space value: {0}", value));
1505 CheckCurrentStateUpdate ();
1508 private void PushElementName (string name
, string local
, string prefix
)
1510 if (elementNames
.Length
== elementNameStackPos
) {
1511 TagName
[] newArray
= new TagName
[elementNames
.Length
* 2];
1512 Array
.Copy (elementNames
, 0, newArray
, 0, elementNameStackPos
);
1513 elementNames
= newArray
;
1515 elementNames
[elementNameStackPos
++] =
1516 new TagName (name
, local
, prefix
);
1519 // The reader is positioned on the first character
1520 // of the element's name.
1521 private void ReadEndTag ()
1523 if (currentState
!= XmlNodeType
.Element
)
1524 throw NotWFError ("End tag cannot appear in this state.");
1526 currentLinkedNodeLineNumber
= line
;
1527 currentLinkedNodeLinePosition
= column
;
1529 if (elementNameStackPos
== 0)
1530 throw NotWFError ("closing element without matching opening element");
1531 TagName expected
= elementNames
[--elementNameStackPos
];
1532 Expect (expected
.Name
);
1534 ExpectAfterWhitespace ('>');
1539 XmlNodeType
.EndElement
, // nodeType
1540 expected
.Name
, // name
1541 expected
.Prefix
, // prefix
1542 expected
.LocalName
, // localName
1543 false, // isEmptyElement
1545 true // clearAttributes
1547 if (expected
.Prefix
.Length
> 0)
1548 currentToken
.NamespaceURI
= LookupNamespace (expected
.Prefix
, true);
1549 else if (namespaces
)
1550 currentToken
.NamespaceURI
= nsmgr
.DefaultNamespace
;
1554 CheckCurrentStateUpdate ();
1557 private void CheckCurrentStateUpdate ()
1559 if (depth
== 0 && !allowMultipleRoot
&& (IsEmptyElement
|| NodeType
== XmlNodeType
.EndElement
))
1560 currentState
= XmlNodeType
.EndElement
;
1564 private void AppendSurrogatePairNameChar (int ch
)
1566 nameBuffer
[nameLength
++] = (char) ((ch
- 0x10000) / 0x400 + 0xD800);
1567 if (nameLength
== nameCapacity
)
1568 ExpandNameCapacity ();
1569 nameBuffer
[nameLength
++] = (char) ((ch
- 0x10000) % 0x400 + 0xDC00);
1572 private void ExpandNameCapacity ()
1574 nameCapacity
= nameCapacity
* 2;
1575 char [] oldNameBuffer
= nameBuffer
;
1576 nameBuffer
= new char [nameCapacity
];
1577 Array
.Copy (oldNameBuffer
, nameBuffer
, nameLength
);
1581 private void AppendValueChar (int ch
)
1583 if (ch
< Char
.MaxValue
)
1584 valueBuffer
.Append ((char) ch
);
1586 AppendSurrogatePairValueChar (ch
);
1589 private void AppendSurrogatePairValueChar (int ch
)
1591 valueBuffer
.Append ((char) ((ch
- 0x10000) / 0x400 + 0xD800));
1592 valueBuffer
.Append ((char) ((ch
- 0x10000) % 0x400 + 0xDC00));
1595 private string CreateValueString ()
1597 // Since whitespace strings are mostly identical
1598 // depending on the Depth, we make use of NameTable
1599 // to atomize whitespace strings.
1601 case XmlNodeType
.Whitespace
:
1602 case XmlNodeType
.SignificantWhitespace
:
1603 int len
= valueBuffer
.Length
;
1604 if (whitespaceCache
== null)
1605 whitespaceCache
= new char [32];
1606 if (len
>= whitespaceCache
.Length
)
1608 if (whitespacePool
== null)
1609 whitespacePool
= new NameTable ();
1610 #if NET_2_0 && !NET_2_1
1611 valueBuffer
.CopyTo (0, whitespaceCache
, 0, len
);
1613 for (int i
= 0; i
< len
; i
++)
1614 whitespaceCache
[i
] = valueBuffer
[i
];
1616 return whitespacePool
.Add (whitespaceCache
, 0, valueBuffer
.Length
);
1618 return (valueBuffer
.Capacity
< 100) ?
1619 valueBuffer
.ToString (0, valueBuffer
.Length
) :
1620 valueBuffer
.ToString ();
1623 private void ClearValueBuffer ()
1625 valueBuffer
.Length
= 0;
1628 // The reader is positioned on the first character
1630 private void ReadText (bool notWhitespace
)
1632 if (currentState
!= XmlNodeType
.Element
)
1633 throw NotWFError ("Text node cannot appear in this state.");
1634 preserveCurrentTag
= false;
1637 ClearValueBuffer ();
1639 int ch
= PeekChar ();
1640 bool previousWasCloseBracket
= false;
1642 while (ch
!= '<' && ch
!= -1) {
1645 ch
= ReadReference (false);
1646 if (returnEntityReference
) // Returns -1 if char validation should not be done
1648 } else if (normalization
&& ch
== '\r') {
1652 // append '\n' instead of '\r'.
1653 AppendValueChar ('\n');
1654 // and in case of "\r\n", discard '\r'.
1657 if (CharacterChecking
&& XmlChar
.IsInvalid (ch
))
1658 throw NotWFError ("Not allowed character was found.");
1662 // FIXME: it might be optimized by the JIT later,
1663 // AppendValueChar (ch);
1665 if (ch
< Char
.MaxValue
)
1666 valueBuffer
.Append ((char) ch
);
1668 AppendSurrogatePairValueChar (ch
);
1673 if (previousWasCloseBracket
)
1674 if (PeekChar () == '>')
1675 throw NotWFError ("Inside text content, character sequence ']]>' is not allowed.");
1676 previousWasCloseBracket
= true;
1678 else if (previousWasCloseBracket
)
1679 previousWasCloseBracket
= false;
1681 notWhitespace
= true;
1684 if (returnEntityReference
&& valueBuffer
.Length
== 0) {
1685 SetEntityReferenceProperties ();
1687 XmlNodeType nodeType
= notWhitespace
? XmlNodeType
.Text
:
1688 this.XmlSpace
== XmlSpace
.Preserve
? XmlNodeType
.SignificantWhitespace
: XmlNodeType
.Whitespace
;
1690 nodeType
, // nodeType
1691 String
.Empty
, // name
1692 String
.Empty
, // prefix
1693 String
.Empty
, // localName
1694 false, // isEmptyElement
1695 null, // value: create only when required
1696 true // clearAttributes
1701 // The leading '&' has already been consumed.
1702 // Returns true if the entity reference isn't a simple
1703 // character reference or one of the predefined entities.
1704 // This allows the ReadText method to break so that the
1705 // next call to Read will return the EntityReference node.
1706 private int ReadReference (bool ignoreEntityReferences
)
1708 if (PeekChar () == '#') {
1710 return ReadCharacterReference ();
1712 return ReadEntityReference (ignoreEntityReferences
);
1715 private int ReadCharacterReference ()
1720 if (PeekChar () == 'x') {
1723 while ((ch
= PeekChar ()) != ';' && ch
!= -1) {
1726 if (ch
>= '0' && ch
<= '9')
1727 value = (value << 4) + ch
- '0';
1728 else if (ch
>= 'A' && ch
<= 'F')
1729 value = (value << 4) + ch
- 'A' + 10;
1730 else if (ch
>= 'a' && ch
<= 'f')
1731 value = (value << 4) + ch
- 'a' + 10;
1733 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
,
1734 "invalid hexadecimal digit: {0} (#x{1:X})",
1739 while ((ch
= PeekChar ()) != ';' && ch
!= -1) {
1742 if (ch
>= '0' && ch
<= '9')
1743 value = value * 10 + ch
- '0';
1745 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
,
1746 "invalid decimal digit: {0} (#x{1:X})",
1754 // There is no way to save surrogate pairs...
1755 if (CharacterChecking
&& Normalization
&&
1756 XmlChar
.IsInvalid (value))
1757 throw NotWFError ("Referenced character was not allowed in XML. Normalization is " + normalization
+ ", checkCharacters = " + checkCharacters
);
1761 // Returns -1 if it should not be validated.
1762 // Real EOF must not be detected here.
1763 private int ReadEntityReference (bool ignoreEntityReferences
)
1765 string name
= ReadName ();
1768 int predefined
= XmlChar
.GetPredefinedEntity (name
);
1769 if (predefined
>= 0)
1772 if (ignoreEntityReferences
) {
1773 AppendValueChar ('&');
1774 for (int i
= 0; i
< name
.Length
; i
++)
1775 AppendValueChar (name
[i
]);
1776 AppendValueChar (';');
1778 returnEntityReference
= true;
1779 entityReferenceName
= name
;
1785 // The reader is positioned on the first character of
1786 // the attribute name.
1787 private void ReadAttributes (bool isXmlDecl
)
1790 bool requireWhitespace
= false;
1791 currentAttribute
= -1;
1792 currentAttributeValue
= -1;
1795 if (!SkipWhitespace () && requireWhitespace
)
1796 throw NotWFError ("Unexpected token. Name is required here.");
1798 IncrementAttributeToken ();
1799 currentAttributeToken
.LineNumber
= line
;
1800 currentAttributeToken
.LinePosition
= column
;
1802 string prefix
, localName
;
1803 currentAttributeToken
.Name
= ReadName (out prefix
, out localName
);
1804 currentAttributeToken
.Prefix
= prefix
;
1805 currentAttributeToken
.LocalName
= localName
;
1806 ExpectAfterWhitespace ('=');
1808 ReadAttributeValueTokens (-1);
1809 // This hack is required for xmldecl which has
1810 // both effective attributes and Value.
1813 dummyValue
= currentAttributeToken
.Value
;
1817 if (!SkipWhitespace ())
1818 requireWhitespace
= true;
1819 peekChar
= PeekChar ();
1821 if (peekChar
== '?')
1824 else if (peekChar
== '/' || peekChar
== '>')
1826 } while (peekChar
!= -1);
1828 currentAttribute
= -1;
1829 currentAttributeValue
= -1;
1832 private void AddAttributeWithValue (string name
, string value)
1834 IncrementAttributeToken ();
1835 XmlAttributeTokenInfo ati
= attributeTokens
[currentAttribute
];
1836 ati
.Name
= NameTable
.Add (name
);
1837 ati
.Prefix
= String
.Empty
;
1838 ati
.NamespaceURI
= String
.Empty
;
1839 IncrementAttributeValueToken ();
1840 XmlTokenInfo vti
= attributeValueTokens
[currentAttributeValue
];
1841 SetTokenProperties (vti
,
1853 private void IncrementAttributeToken ()
1856 if (attributeTokens
.Length
== currentAttribute
) {
1857 XmlAttributeTokenInfo
[] newArray
=
1858 new XmlAttributeTokenInfo
[attributeTokens
.Length
* 2];
1859 attributeTokens
.CopyTo (newArray
, 0);
1860 attributeTokens
= newArray
;
1862 if (attributeTokens
[currentAttribute
] == null)
1863 attributeTokens
[currentAttribute
] = new XmlAttributeTokenInfo (this);
1864 currentAttributeToken
= attributeTokens
[currentAttribute
];
1865 currentAttributeToken
.Clear ();
1868 private void IncrementAttributeValueToken ()
1870 currentAttributeValue
++;
1871 if (attributeValueTokens
.Length
== currentAttributeValue
) {
1872 XmlTokenInfo
[] newArray
= new XmlTokenInfo
[attributeValueTokens
.Length
* 2];
1873 attributeValueTokens
.CopyTo (newArray
, 0);
1874 attributeValueTokens
= newArray
;
1876 if (attributeValueTokens
[currentAttributeValue
] == null)
1877 attributeValueTokens
[currentAttributeValue
] = new XmlTokenInfo (this);
1878 currentAttributeValueToken
= attributeValueTokens
[currentAttributeValue
];
1879 currentAttributeValueToken
.Clear ();
1882 // LAMESPEC: Orthodox XML reader should normalize attribute values
1883 private void ReadAttributeValueTokens (int dummyQuoteChar
)
1885 int quoteChar
= (dummyQuoteChar
< 0) ? ReadChar () : dummyQuoteChar
;
1887 if (quoteChar
!= '\'' && quoteChar
!= '\"')
1888 throw NotWFError ("an attribute value was not quoted");
1889 currentAttributeToken
.QuoteChar
= (char) quoteChar
;
1891 IncrementAttributeValueToken ();
1892 currentAttributeToken
.ValueTokenStartIndex
= currentAttributeValue
;
1893 currentAttributeValueToken
.LineNumber
= line
;
1894 currentAttributeValueToken
.LinePosition
= column
;
1896 bool incrementToken
= false;
1897 bool isNewToken
= true;
1900 currentAttributeValueToken
.ValueBufferStart
= valueBuffer
.Length
;
1903 if (ch
== quoteChar
)
1906 if (incrementToken
) {
1907 IncrementAttributeValueToken ();
1908 currentAttributeValueToken
.ValueBufferStart
= valueBuffer
.Length
;
1909 currentAttributeValueToken
.LineNumber
= line
;
1910 currentAttributeValueToken
.LinePosition
= column
;
1911 incrementToken
= false;
1918 throw NotWFError ("attribute values cannot contain '<'");
1920 if (dummyQuoteChar
< 0)
1921 throw NotWFError ("unexpected end of file in an attribute value");
1922 else // Attribute value constructor.
1928 if (PeekChar () == '\n')
1929 continue; // skip '\r'.
1931 // The csc in MS.NET 2.0 beta 1 barfs on this goto, so work around that
1940 // When Normalize = true, then replace
1941 // all spaces to ' '
1947 if (PeekChar () == '#') {
1949 ch
= ReadCharacterReference ();
1950 AppendValueChar (ch
);
1953 // Check XML 1.0 section 3.1 WFC.
1954 string entName
= ReadName ();
1956 int predefined
= XmlChar
.GetPredefinedEntity (entName
);
1957 if (predefined
< 0) {
1958 CheckAttributeEntityReferenceWFC (entName
);
1960 if (entityHandling
== EntityHandling
.ExpandEntities
) {
1961 string value = DTD
.GenerateEntityAttributeText (entName
);
1962 foreach (char c
in (IEnumerable
<char>) value)
1963 AppendValueChar (c
);
1967 currentAttributeValueToken
.ValueBufferEnd
= valueBuffer
.Length
;
1968 currentAttributeValueToken
.NodeType
= XmlNodeType
.Text
;
1970 IncrementAttributeValueToken ();
1971 currentAttributeValueToken
.Name
= entName
;
1972 currentAttributeValueToken
.Value
= String
.Empty
;
1973 currentAttributeValueToken
.NodeType
= XmlNodeType
.EntityReference
;
1974 incrementToken
= true;
1978 AppendValueChar (predefined
);
1981 if (CharacterChecking
&& XmlChar
.IsInvalid (ch
))
1982 throw NotWFError ("Invalid character was found.");
1983 // FIXME: it might be optimized by the JIT later,
1984 // AppendValueChar (ch);
1986 if (ch
< Char
.MaxValue
)
1987 valueBuffer
.Append ((char) ch
);
1989 AppendSurrogatePairValueChar (ch
);
1996 if (!incrementToken
) {
1997 currentAttributeValueToken
.ValueBufferEnd
= valueBuffer
.Length
;
1998 currentAttributeValueToken
.NodeType
= XmlNodeType
.Text
;
2000 currentAttributeToken
.ValueTokenEndIndex
= currentAttributeValue
;
2004 private void CheckAttributeEntityReferenceWFC (string entName
)
2006 DTDEntityDeclaration entDecl
=
2007 DTD
== null ? null : DTD
.EntityDecls
[entName
];
2008 if (entDecl
== null) {
2009 if (entityHandling
== EntityHandling
.ExpandEntities
2010 || (DTD
!= null && resolver
!= null && entDecl
== null))
2011 throw NotWFError (String
.Format ("Referenced entity '{0}' does not exist.", entName
));
2016 if (entDecl
.HasExternalReference
)
2017 throw NotWFError ("Reference to external entities is not allowed in the value of an attribute.");
2018 if (isStandalone
&& !entDecl
.IsInternalSubset
)
2019 throw NotWFError ("Reference to external entities is not allowed in the internal subset.");
2020 if (entDecl
.EntityValue
.IndexOf ('<') >= 0)
2021 throw NotWFError ("Attribute must not contain character '<' either directly or indirectly by way of entity references.");
2024 // The reader is positioned on the first character
2027 // It may be xml declaration or processing instruction.
2028 private void ReadProcessingInstruction ()
2030 string target
= ReadName ();
2031 if (target
!= "xml" && target
.ToLower (CultureInfo
.InvariantCulture
) == "xml")
2032 throw NotWFError ("Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
2034 if (!SkipWhitespace ())
2035 if (PeekChar () != '?')
2036 throw NotWFError ("Invalid processing instruction name was found.");
2038 ClearValueBuffer ();
2041 while ((ch
= PeekChar ()) != -1) {
2044 if (ch
== '?' && PeekChar () == '>') {
2049 if (CharacterChecking
&& XmlChar
.IsInvalid (ch
))
2050 throw NotWFError ("Invalid character was found.");
2051 AppendValueChar (ch
);
2054 if (Object
.ReferenceEquals (target
, XmlNamespaceManager
.PrefixXml
))
2055 VerifyXmlDeclaration ();
2057 if (currentState
== XmlNodeType
.None
)
2058 currentState
= XmlNodeType
.XmlDeclaration
;
2061 XmlNodeType
.ProcessingInstruction
, // nodeType
2063 String
.Empty
, // prefix
2064 target
, // localName
2065 false, // isEmptyElement
2066 null, // value: create only when required
2067 true // clearAttributes
2072 void VerifyXmlDeclaration ()
2074 if (!allowMultipleRoot
&& currentState
!= XmlNodeType
.None
)
2075 throw NotWFError ("XML declaration cannot appear in this state.");
2077 currentState
= XmlNodeType
.XmlDeclaration
;
2079 string text
= CreateValueString ();
2085 string encoding
= null, standalone
= null;
2087 ParseAttributeFromString (text
, ref idx
, out name
, out value);
2088 if (name
!= "version" || value != "1.0")
2089 throw NotWFError ("'version' is expected.");
2090 name
= String
.Empty
;
2091 if (SkipWhitespaceInString (text
, ref idx
) && idx
< text
.Length
)
2092 ParseAttributeFromString (text
, ref idx
, out name
, out value);
2093 if (name
== "encoding") {
2094 if (!XmlChar
.IsValidIANAEncoding (value))
2095 throw NotWFError ("'encoding' must be a valid IANA encoding name.");
2096 if (reader
is XmlStreamReader
)
2097 parserContext
.Encoding
= ((XmlStreamReader
) reader
).Encoding
;
2099 parserContext
.Encoding
= Encoding
.Unicode
;
2101 name
= String
.Empty
;
2102 if (SkipWhitespaceInString (text
, ref idx
) && idx
< text
.Length
)
2103 ParseAttributeFromString (text
, ref idx
, out name
, out value);
2105 if (name
== "standalone") {
2106 this.isStandalone
= value == "yes";
2107 if (value != "yes" && value != "no")
2108 throw NotWFError ("Only 'yes' or 'no' is allow for 'standalone'");
2110 SkipWhitespaceInString (text
, ref idx
);
2112 else if (name
.Length
!= 0)
2113 throw NotWFError (String
.Format ("Unexpected token: '{0}'", name
));
2115 if (idx
< text
.Length
)
2116 throw NotWFError ("'?' is expected.");
2118 AddAttributeWithValue ("version", "1.0");
2119 if (encoding
!= null)
2120 AddAttributeWithValue ("encoding", encoding
);
2121 if (standalone
!= null)
2122 AddAttributeWithValue ("standalone", standalone
);
2123 currentAttribute
= currentAttributeValue
= -1;
2126 XmlNodeType
.XmlDeclaration
, // nodeType
2128 String
.Empty
, // prefix
2130 false, // isEmptyElement
2132 false // clearAttributes
2136 bool SkipWhitespaceInString (string text
, ref int idx
)
2139 while (idx
< text
.Length
&& XmlChar
.IsWhitespace (text
[idx
]))
2141 return idx
- start
> 0;
2144 private void ParseAttributeFromString (string src
,
2145 ref int idx
, out string name
, out string value)
2147 while (idx
< src
.Length
&& XmlChar
.IsWhitespace (src
[idx
]))
2151 while (idx
< src
.Length
&& XmlChar
.IsNameChar (src
[idx
]))
2153 name
= src
.Substring (start
, idx
- start
);
2155 while (idx
< src
.Length
&& XmlChar
.IsWhitespace (src
[idx
]))
2157 if (idx
== src
.Length
|| src
[idx
] != '=')
2158 throw NotWFError (String
.Format ("'=' is expected after {0}", name
));
2161 while (idx
< src
.Length
&& XmlChar
.IsWhitespace (src
[idx
]))
2164 if (idx
== src
.Length
|| src
[idx
] != '"' && src
[idx
] != '\'')
2165 throw NotWFError ("'\"' or '\'' is expected.");
2167 char quote
= src
[idx
];
2171 while (idx
< src
.Length
&& src
[idx
] != quote
)
2175 value = src
.Substring (start
, idx
- start
- 1);
2178 internal void SkipTextDeclaration ()
2180 if (PeekChar () != '<')
2185 if (PeekChar () != '?') {
2191 while (peekCharsIndex
< 6) {
2192 if (PeekChar () < 0)
2197 if (new string (peekChars
, 2, 4) != "xml ") {
2198 if (new string (peekChars
, 2, 4).ToLower (CultureInfo
.InvariantCulture
) == "xml ") {
2199 throw NotWFError ("Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
2208 if (PeekChar () == 'v') {
2210 ExpectAfterWhitespace ('=');
2212 int quoteChar
= ReadChar ();
2213 char [] expect1_0
= new char [3];
2214 int versionLength
= 0;
2215 switch (quoteChar
) {
2218 while (PeekChar () != quoteChar
) {
2219 if (PeekChar () == -1)
2220 throw NotWFError ("Invalid version declaration inside text declaration.");
2221 else if (versionLength
== 3)
2222 throw NotWFError ("Invalid version number inside text declaration.");
2224 expect1_0
[versionLength
] = (char) ReadChar ();
2226 if (versionLength
== 3 && new String (expect1_0
) != "1.0")
2227 throw NotWFError ("Invalid version number inside text declaration.");
2234 throw NotWFError ("Invalid version declaration inside text declaration.");
2238 if (PeekChar () == 'e') {
2239 Expect ("encoding");
2240 ExpectAfterWhitespace ('=');
2242 int quoteChar
= ReadChar ();
2243 switch (quoteChar
) {
2246 while (PeekChar () != quoteChar
)
2247 if (ReadChar () == -1)
2248 throw NotWFError ("Invalid encoding declaration inside text declaration.");
2253 throw NotWFError ("Invalid encoding declaration inside text declaration.");
2255 // Encoding value should be checked inside XmlInputStream.
2258 // this condition is to check if this instance is
2259 // not created by XmlReader.Create() (which just
2260 // omits strict text declaration check).
2261 else if (Conformance
== ConformanceLevel
.Auto
)
2262 throw NotWFError ("Encoding declaration is mandatory in text declaration.");
2267 curNodePeekIndex
= peekCharsIndex
; // without this it causes incorrect value start indication.
2270 // The reader is positioned on the first character after
2271 // the leading '<!'.
2272 private void ReadDeclaration ()
2274 int ch
= PeekChar ();
2292 throw NotWFError ("Unexpected declaration markup was found.");
2296 // The reader is positioned on the first character after
2297 // the leading '<!--'.
2298 private void ReadComment ()
2300 if (currentState
== XmlNodeType
.None
)
2301 currentState
= XmlNodeType
.XmlDeclaration
;
2303 preserveCurrentTag
= false;
2305 ClearValueBuffer ();
2308 while ((ch
= PeekChar ()) != -1) {
2311 if (ch
== '-' && PeekChar () == '-') {
2314 if (PeekChar () != '>')
2315 throw NotWFError ("comments cannot contain '--'");
2321 if (XmlChar
.IsInvalid (ch
))
2322 throw NotWFError ("Not allowed character was found.");
2324 AppendValueChar (ch
);
2328 XmlNodeType
.Comment
, // nodeType
2329 String
.Empty
, // name
2330 String
.Empty
, // prefix
2331 String
.Empty
, // localName
2332 false, // isEmptyElement
2333 null, // value: create only when required
2334 true // clearAttributes
2338 // The reader is positioned on the first character after
2339 // the leading '<![CDATA['.
2340 private void ReadCDATA ()
2342 if (currentState
!= XmlNodeType
.Element
)
2343 throw NotWFError ("CDATA section cannot appear in this state.");
2344 preserveCurrentTag
= false;
2346 ClearValueBuffer ();
2350 while (PeekChar () != -1) {
2355 if (ch
== ']' && PeekChar () == ']') {
2356 ch
= ReadChar (); // ']'
2358 if (PeekChar () == '>') {
2365 if (normalization
&& ch
== '\r') {
2368 // append '\n' instead of '\r'.
2369 AppendValueChar ('\n');
2370 // otherwise, discard '\r'.
2373 if (CharacterChecking
&& XmlChar
.IsInvalid (ch
))
2374 throw NotWFError ("Invalid character was found.");
2376 // FIXME: it might be optimized by the JIT later,
2377 // AppendValueChar (ch);
2379 if (ch
< Char
.MaxValue
)
2380 valueBuffer
.Append ((char) ch
);
2382 AppendSurrogatePairValueChar (ch
);
2387 XmlNodeType
.CDATA
, // nodeType
2388 String
.Empty
, // name
2389 String
.Empty
, // prefix
2390 String
.Empty
, // localName
2391 false, // isEmptyElement
2392 null, // value: create only when required
2393 true // clearAttributes
2397 // The reader is positioned on the first character after
2398 // the leading '<!DOCTYPE'.
2399 private void ReadDoctypeDecl ()
2402 throw NotWFError ("Document Type Declaration (DTD) is prohibited in this XML.");
2403 switch (currentState
) {
2404 case XmlNodeType
.DocumentType
:
2405 case XmlNodeType
.Element
:
2406 case XmlNodeType
.EndElement
:
2407 throw NotWFError ("Document type cannot appear in this state.");
2409 currentState
= XmlNodeType
.DocumentType
;
2411 string doctypeName
= null;
2412 string publicId
= null;
2413 string systemId
= null;
2414 int intSubsetStartLine
= 0;
2415 int intSubsetStartColumn
= 0;
2418 doctypeName
= ReadName ();
2423 systemId
= ReadSystemLiteral (true);
2426 publicId
= ReadPubidLiteral ();
2427 if (!SkipWhitespace ())
2428 throw NotWFError ("Whitespace is required between PUBLIC id and SYSTEM id.");
2429 systemId
= ReadSystemLiteral (false);
2435 if(PeekChar () == '[')
2437 // read markupdecl etc. or end of decl
2439 intSubsetStartLine
= this.LineNumber
;
2440 intSubsetStartColumn
= this.LinePosition
;
2441 ClearValueBuffer ();
2442 ReadInternalSubset ();
2443 parserContext
.InternalSubset
= CreateValueString ();
2445 // end of DOCTYPE decl.
2446 ExpectAfterWhitespace ('>');
2448 GenerateDTDObjectModel (doctypeName
, publicId
,
2449 systemId
, parserContext
.InternalSubset
,
2450 intSubsetStartLine
, intSubsetStartColumn
);
2452 // set properties for <!DOCTYPE> node
2454 XmlNodeType
.DocumentType
, // nodeType
2455 doctypeName
, // name
2456 String
.Empty
, // prefix
2457 doctypeName
, // localName
2458 false, // isEmptyElement
2459 parserContext
.InternalSubset
, // value
2460 true // clearAttributes
2463 if (publicId
!= null)
2464 AddAttributeWithValue ("PUBLIC", publicId
);
2465 if (systemId
!= null)
2466 AddAttributeWithValue ("SYSTEM", systemId
);
2467 currentAttribute
= currentAttributeValue
= -1;
2470 internal DTDObjectModel
GenerateDTDObjectModel (string name
, string publicId
,
2471 string systemId
, string internalSubset
)
2473 return GenerateDTDObjectModel (name
, publicId
, systemId
, internalSubset
, 0, 0);
2476 internal DTDObjectModel
GenerateDTDObjectModel (string name
, string publicId
,
2477 string systemId
, string internalSubset
, int intSubsetStartLine
, int intSubsetStartColumn
)
2480 parserContext
.Dtd
= new DTDObjectModel (this.NameTable
); // merges both internal and external subsets in the meantime,
2481 DTD
.BaseURI
= BaseURI
;
2483 DTD
.PublicId
= publicId
;
2484 DTD
.SystemId
= systemId
;
2485 DTD
.InternalSubset
= internalSubset
;
2486 DTD
.XmlResolver
= resolver
;
2487 DTD
.IsStandalone
= isStandalone
;
2488 DTD
.LineNumber
= line
;
2489 DTD
.LinePosition
= column
;
2491 DTDReader dr
= new DTDReader (DTD
, intSubsetStartLine
, intSubsetStartColumn
);
2492 dr
.Normalization
= this.normalization
;
2493 return dr
.GenerateDTDObjectModel ();
2496 private enum DtdInputState
2509 private class DtdInputStateStack
2511 Stack intern
= new Stack ();
2512 public DtdInputStateStack ()
2514 Push (DtdInputState
.Free
);
2517 public DtdInputState
Peek ()
2519 return (DtdInputState
) intern
.Peek ();
2522 public DtdInputState
Pop ()
2524 return (DtdInputState
) intern
.Pop ();
2527 public void Push (DtdInputState val
)
2534 DtdInputStateStack stateStack
= new DtdInputStateStack ();
2535 DtdInputState State
{
2536 get { return stateStack.Peek (); }
2539 private int ReadValueChar ()
2541 int ret
= ReadChar ();
2542 AppendValueChar (ret
);
2546 private void ExpectAndAppend (string s
)
2549 valueBuffer
.Append (s
);
2552 // Simply read but not generate any result.
2553 private void ReadInternalSubset ()
2555 bool continueParse
= true;
2557 while (continueParse
) {
2558 switch (ReadValueChar ()) {
2561 case DtdInputState
.Free
:
2563 valueBuffer
.Remove (valueBuffer
.Length
- 1, 1);
2564 continueParse
= false;
2566 case DtdInputState
.InsideDoubleQuoted
:
2567 case DtdInputState
.InsideSingleQuoted
:
2568 case DtdInputState
.Comment
:
2571 throw NotWFError ("unexpected end of file at DTD.");
2575 throw NotWFError ("unexpected end of file at DTD.");
2578 case DtdInputState
.InsideDoubleQuoted
:
2579 case DtdInputState
.InsideSingleQuoted
:
2580 case DtdInputState
.Comment
:
2581 continue; // well-formed
2583 int c
= ReadValueChar ();
2586 stateStack
.Push (DtdInputState
.PI
);
2589 switch (ReadValueChar ()) {
2591 switch (ReadValueChar ()) {
2593 ExpectAndAppend ("EMENT");
2594 stateStack
.Push (DtdInputState
.ElementDecl
);
2597 ExpectAndAppend ("TITY");
2598 stateStack
.Push (DtdInputState
.EntityDecl
);
2601 throw NotWFError ("unexpected token '<!E'.");
2605 ExpectAndAppend ("TTLIST");
2606 stateStack
.Push (DtdInputState
.AttlistDecl
);
2609 ExpectAndAppend ("OTATION");
2610 stateStack
.Push (DtdInputState
.NotationDecl
);
2613 ExpectAndAppend ("-");
2614 stateStack
.Push (DtdInputState
.Comment
);
2619 throw NotWFError (String
.Format ("unexpected '<{0}'.", (char) c
));
2623 if (State
== DtdInputState
.InsideSingleQuoted
)
2625 else if (State
!= DtdInputState
.InsideDoubleQuoted
&& State
!= DtdInputState
.Comment
)
2626 stateStack
.Push (DtdInputState
.InsideSingleQuoted
);
2629 if (State
== DtdInputState
.InsideDoubleQuoted
)
2631 else if (State
!= DtdInputState
.InsideSingleQuoted
&& State
!= DtdInputState
.Comment
)
2632 stateStack
.Push (DtdInputState
.InsideDoubleQuoted
);
2636 case DtdInputState
.ElementDecl
:
2637 goto case DtdInputState
.NotationDecl
;
2638 case DtdInputState
.AttlistDecl
:
2639 goto case DtdInputState
.NotationDecl
;
2640 case DtdInputState
.EntityDecl
:
2641 goto case DtdInputState
.NotationDecl
;
2642 case DtdInputState
.NotationDecl
:
2645 case DtdInputState
.InsideDoubleQuoted
:
2646 case DtdInputState
.InsideSingleQuoted
:
2647 case DtdInputState
.Comment
:
2650 throw NotWFError ("unexpected token '>'");
2654 if (State
== DtdInputState
.PI
) {
2655 if (ReadValueChar () == '>')
2660 if (State
== DtdInputState
.Comment
) {
2661 if (PeekChar () == '-') {
2663 ExpectAndAppend (">");
2669 if (State
!= DtdInputState
.Free
&& State
!= DtdInputState
.EntityDecl
&& State
!= DtdInputState
.Comment
&& State
!= DtdInputState
.InsideDoubleQuoted
&& State
!= DtdInputState
.InsideSingleQuoted
)
2670 throw NotWFError ("Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2676 // The reader is positioned on the first 'S' of "SYSTEM".
2677 private string ReadSystemLiteral (bool expectSYSTEM
)
2681 if (!SkipWhitespace ())
2682 throw NotWFError ("Whitespace is required after 'SYSTEM'.");
2686 int quoteChar
= ReadChar (); // apos or quot
2688 ClearValueBuffer ();
2689 while (c
!= quoteChar
) {
2692 throw NotWFError ("Unexpected end of stream in ExternalID.");
2694 AppendValueChar (c
);
2696 return CreateValueString ();
2699 private string ReadPubidLiteral()
2702 if (!SkipWhitespace ())
2703 throw NotWFError ("Whitespace is required after 'PUBLIC'.");
2704 int quoteChar
= ReadChar ();
2706 ClearValueBuffer ();
2707 while(c
!= quoteChar
)
2710 if(c
< 0) throw NotWFError ("Unexpected end of stream in ExternalID.");
2711 if(c
!= quoteChar
&& !XmlChar
.IsPubidChar (c
))
2712 throw NotWFError (String
.Format ("character '{0}' not allowed for PUBLIC ID", (char)c
));
2714 AppendValueChar (c
);
2716 return CreateValueString ();
2719 // The reader is positioned on the first character
2721 private string ReadName ()
2723 string prefix
, local
;
2724 return ReadName (out prefix
, out local
);
2727 private string ReadName (out string prefix
, out string localName
)
2729 #if !USE_NAME_BUFFER
2730 bool savePreserve
= preserveCurrentTag
;
2731 preserveCurrentTag
= true;
2733 int startOffset
= peekCharsIndex
- curNodePeekIndex
;
2734 int ch
= PeekChar ();
2735 if (!XmlChar
.IsFirstNameChar (ch
))
2736 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
, "a name did not start with a legal character {0} ({1})", ch
, (char) ch
));
2741 while (XmlChar
.IsNameChar ((ch
= PeekChar ()))) {
2743 if (ch
== ':' && namespaces
&& colonAt
< 0)
2748 int start
= curNodePeekIndex
+ startOffset
;
2750 string name
= NameTable
.Add (
2751 peekChars
, start
, length
);
2754 prefix
= NameTable
.Add (
2755 peekChars
, start
, colonAt
);
2756 localName
= NameTable
.Add (
2757 peekChars
, start
+ colonAt
+ 1, length
- colonAt
- 1);
2759 prefix
= String
.Empty
;
2763 preserveCurrentTag
= savePreserve
;
2767 int ch
= PeekChar ();
2768 if (!XmlChar
.IsFirstNameChar (ch
))
2769 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
, "a name did not start with a legal character {0} ({1})", ch
, (char) ch
));
2774 // AppendNameChar (ch);
2776 // nameBuffer.Length is always non-0 so no need to ExpandNameCapacity () here
2777 if (ch
< Char
.MaxValue
)
2778 nameBuffer
[nameLength
++] = (char) ch
;
2780 AppendSurrogatePairNameChar (ch
);
2785 while (XmlChar
.IsNameChar ((ch
= PeekChar ()))) {
2788 if (ch
== ':' && namespaces
&& colonAt
< 0)
2789 colonAt
= nameLength
;
2790 // AppendNameChar (ch);
2792 if (nameLength
== nameCapacity
)
2793 ExpandNameCapacity ();
2794 if (ch
< Char
.MaxValue
)
2795 nameBuffer
[nameLength
++] = (char) ch
;
2797 AppendSurrogatePairNameChar (ch
);
2801 string name
= NameTable
.Add (nameBuffer
, 0, nameLength
);
2804 prefix
= NameTable
.Add (nameBuffer
, 0, colonAt
);
2805 localName
= NameTable
.Add (nameBuffer
, colonAt
+ 1, nameLength
- colonAt
- 1);
2807 prefix
= String
.Empty
;
2815 // Read the next character and compare it against the
2816 // specified character.
2817 private void Expect (int expected
)
2819 int ch
= ReadChar ();
2821 if (ch
!= expected
) {
2822 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
,
2823 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2826 ch
< 0 ? (object) "EOF" : (char) ch
,
2831 private void Expect (string expected
)
2833 for (int i
= 0; i
< expected
.Length
; i
++)
2834 if (ReadChar () != expected
[i
])
2835 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
,
2836 "'{0}' is expected", expected
));
2839 private void ExpectAfterWhitespace (char c
)
2842 int i
= ReadChar ();
2843 if (i
< 0x21 && XmlChar
.IsWhitespace (i
))
2846 throw NotWFError (String
.Format (CultureInfo
.InvariantCulture
, "Expected {0}, but found {1} [{2}]", c
, i
< 0 ? (object) "EOF" : (char) i
, i
));
2851 // Does not consume the first non-whitespace character.
2852 private bool SkipWhitespace ()
2854 // FIXME: It should be inlined by the JIT.
2855 // bool skipped = XmlChar.IsWhitespace (PeekChar ());
2856 int ch
= PeekChar ();
2857 bool skipped
= (ch
== 0x20 || ch
== 0x9 || ch
== 0xA || ch
== 0xD);
2861 // FIXME: It should be inlined by the JIT.
2862 // while (XmlChar.IsWhitespace (PeekChar ()))
2864 while ((ch
= PeekChar ()) == 0x20 || ch
== 0x9 || ch
== 0xA || ch
== 0xD)
2869 private bool ReadWhitespace ()
2871 if (currentState
== XmlNodeType
.None
)
2872 currentState
= XmlNodeType
.XmlDeclaration
;
2874 bool savePreserve
= preserveCurrentTag
;
2875 preserveCurrentTag
= true;
2876 int startOffset
= peekCharsIndex
- curNodePeekIndex
; // it should be 0 for now though.
2878 int ch
= PeekChar ();
2882 // FIXME: It should be inlined by the JIT.
2883 // } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2884 } while (ch
== 0x20 || ch
== 0x9 || ch
== 0xA || ch
== 0xD);
2886 bool isText
= currentState
== XmlNodeType
.Element
&& ch
!= -1 && ch
!= '<';
2888 if (!isText
&& (whitespaceHandling
== WhitespaceHandling
.None
||
2889 whitespaceHandling
== WhitespaceHandling
.Significant
&& XmlSpace
!= XmlSpace
.Preserve
))
2892 ClearValueBuffer ();
2893 valueBuffer
.Append (peekChars
, curNodePeekIndex
, peekCharsIndex
- curNodePeekIndex
- startOffset
);
2894 preserveCurrentTag
= savePreserve
;
2899 XmlNodeType nodeType
= (this.XmlSpace
== XmlSpace
.Preserve
) ?
2900 XmlNodeType
.SignificantWhitespace
: XmlNodeType
.Whitespace
;
2901 SetProperties (nodeType
,
2906 null, // value: create only when required
2913 // Returns -1 if it should throw an error.
2914 private int ReadCharsInternal (char [] buffer
, int offset
, int length
)
2916 int bufIndex
= offset
;
2917 for (int i
= 0; i
< length
; i
++) {
2918 int c
= PeekChar ();
2921 throw NotWFError ("Unexpected end of xml.");
2923 if (i
+ 1 == length
)
2924 // if it does not end here,
2925 // it cannot store another
2926 // character, so stop here.
2929 if (PeekChar () != '/') {
2931 buffer
[bufIndex
++] = '<';
2934 else if (nestLevel
-- > 0) {
2935 buffer
[bufIndex
++] = '<';
2938 // Seems to skip immediate EndElement
2945 readCharsInProgress
= false;
2946 Read (); // move to the next node
2950 if (c
< Char
.MaxValue
)
2951 buffer
[bufIndex
++] = (char) c
;
2953 buffer
[bufIndex
++] = (char) ((c
- 0x10000) / 0x400 + 0xD800);
2954 buffer
[bufIndex
++] = (char) ((c
- 0x10000) % 0x400 + 0xDC00);
2962 private bool ReadUntilEndTag ()
2965 currentState
= XmlNodeType
.EndElement
;
2971 throw NotWFError ("Unexpected end of xml.");
2973 if (PeekChar () != '/') {
2977 else if (--nestLevel
> 0)
2980 string name
= ReadName ();
2981 if (name
!= elementNames
[elementNameStackPos
- 1].Name
)