Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Xml / System / Xml / Core / XmlTextReaderImpl.cs / 1305376 / XmlTextReaderImpl.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// [....]
//-----------------------------------------------------------------------------
using System;
using System.IO;
using System.Text;
using System.Security;
using System.Threading;
using System.Xml.Schema;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
#if SILVERLIGHT
using System.Reflection;
#endif
#if SILVERLIGHT
using BufferBuilder=System.Xml.BufferBuilder;
#else
using BufferBuilder = System.Text.StringBuilder;
#endif
namespace System.Xml {
internal partial class XmlTextReaderImpl : XmlReader, IXmlLineInfo, IXmlNamespaceResolver {
//
// Private helper types
//
// ParsingFunction = what should the reader do when the next Read() is called
enum ParsingFunction {
ElementContent = 0,
NoData,
#if !SILVERLIGHT
OpenUrl,
#endif
SwitchToInteractive,
SwitchToInteractiveXmlDecl,
DocumentContent,
MoveToElementContent,
PopElementContext,
PopEmptyElementContext,
ResetAttributesRootLevel,
Error,
Eof,
ReaderClosed,
EntityReference,
InIncrementalRead,
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
FragmentAttribute,
ReportEndEntity,
AfterResolveEntityInContent,
AfterResolveEmptyEntityInContent,
#endif
XmlDeclarationFragment,
GoToEof,
PartialTextValue,
// these two states must be last; see InAttributeValueIterator property
InReadAttributeValue,
InReadValueChunk,
InReadContentAsBinary,
InReadElementContentAsBinary,
}
enum ParsingMode {
Full,
SkipNode,
SkipContent,
}
enum EntityType {
CharacterDec,
CharacterHex,
CharacterNamed,
Expanded,
Skipped,
FakeExpanded,
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
Unexpanded,
ExpandedInAttribute,
#endif
}
enum EntityExpandType {
All,
OnlyGeneral,
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
OnlyCharacter,
#endif
}
enum IncrementalReadState {
// Following values are used in ReadText, ReadBase64 and ReadBinHex (V1 streaming methods)
Text,
StartTag,
PI,
CDATA,
Comment,
Attributes,
AttributeValue,
ReadData,
EndElement,
End,
// Following values are used in ReadTextChunk, ReadContentAsBase64 and ReadBinHexChunk (V2 streaming methods)
ReadValueChunk_OnCachedValue,
ReadValueChunk_OnPartialValue,
ReadContentAsBinary_OnCachedValue,
ReadContentAsBinary_OnPartialValue,
ReadContentAsBinary_End,
}
//
// Fields
//
// XmlCharType instance
XmlCharType xmlCharType = XmlCharType.Instance;
// current parsing state (aka. scanner data)
ParsingState ps;
// parsing function = what to do in the next Read() (3-items-long stack, usually used just 2 level)
ParsingFunction parsingFunction;
ParsingFunction nextParsingFunction;
ParsingFunction nextNextParsingFunction;
// stack of nodes
NodeData[] nodes;
// current node
NodeData curNode;
// current index
int index = 0;
// attributes info
int curAttrIndex = -1;
int attrCount;
int attrHashtable;
int attrDuplWalkCount;
bool attrNeedNamespaceLookup;
bool fullAttrCleanup;
NodeData[] attrDuplSortingArray;
// name table
XmlNameTable nameTable;
bool nameTableFromSettings;
// resolver
XmlResolver xmlResolver;
#if !SILVERLIGHT // Needed only for XmlTextReader constructors that takes url
// this is only for constructors that takes url
string url = string.Empty;
CompressedStack compressedStack;
#endif
// settings
bool normalize;
bool supportNamespaces = true;
WhitespaceHandling whitespaceHandling;
DtdProcessing dtdProcessing = DtdProcessing.Parse;
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
EntityHandling entityHandling;
#endif
bool ignorePIs;
bool ignoreComments;
bool checkCharacters;
int lineNumberOffset;
int linePositionOffset;
bool closeInput;
long maxCharactersInDocument;
long maxCharactersFromEntities;
// this flag enables XmlTextReader backwards compatibility;
// when false, the reader has been created via XmlReader.Create
bool v1Compat;
// namespace handling
XmlNamespaceManager namespaceManager;
string lastPrefix = string.Empty;
// xml context (xml:space, xml:lang, default namespace)
XmlContext xmlContext;
// stack of parsing states (=stack of entities)
private ParsingState[] parsingStatesStack;
private int parsingStatesStackTop = -1;
// current node base uri and encoding
string reportedBaseUri;
Encoding reportedEncoding;
// DTD
IDtdInfo dtdInfo;
// fragment parsing
XmlNodeType fragmentType = XmlNodeType.Document;
XmlParserContext fragmentParserContext;
#if !SILVERLIGHT // Needed only for XmlTextReader
bool fragment;
#endif
// incremental read
IncrementalReadDecoder incReadDecoder;
IncrementalReadState incReadState;
LineInfo incReadLineInfo;
BinHexDecoder binHexDecoder;
Base64Decoder base64Decoder;
#if !SILVERLIGHT // Needed only for XmlTextReader (ReadChars, ReadBase64, ReadBinHex)
int incReadDepth;
int incReadLeftStartPos;
int incReadLeftEndPos;
IncrementalReadCharsDecoder readCharsDecoder;
#endif
// ReadAttributeValue helpers
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
int attributeValueBaseEntityId;
bool emptyEntityInAttributeResolved;
#endif
// Validation helpers
#if !SILVERLIGHT // No validation in Silverlight
IValidationEventHandling validationEventHandling;
OnDefaultAttributeUseDelegate onDefaultAttributeUse;
#endif
#if !SILVERLIGHT // Needed only for XmlTextReader and XmlValidatingReader
bool validatingReaderCompatFlag;
#endif
// misc
bool addDefaultAttributesAndNormalize;
BufferBuilder stringBuilder;
bool rootElementParsed;
bool standalone;
int nextEntityId = 1;
ParsingMode parsingMode;
ReadState readState = ReadState.Initial;
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities, ResetState)
IDtdEntityInfo lastEntity;
bool afterResetState;
#endif
int documentStartBytePos;
int readValueOffset;
// Counters for security settings
long charactersInDocument;
long charactersFromEntities;
// All entities that are currently being processed
Dictionary currentEntities;
// DOM helpers
#if !SILVERLIGHT // Needed only for XmlTextReader (when used from XmlDocument)
bool disableUndeclaredEntityCheck;
#endif
// Outer XmlReader exposed to the user - either XmlTextReader or XmlTextReaderImpl (when created via XmlReader.Create).
// Virtual methods called from within XmlTextReaderImpl must be called on the outer reader so in case the user overrides
// some of the XmlTextReader methods we will call the overriden version.
XmlReader outerReader;
//
// Atomized string constants
//
private string Xml;
private string XmlNs;
//
// Constants
//
private const int MaxBytesToMove = 128;
private const int ApproxXmlDeclLength = 80;
private const int NodesInitialSize = 8;
private const int InitialAttributesCount = 4;
private const int InitialParsingStateStackSize = 2;
private const int InitialParsingStatesDepth = 2;
private const int DtdChidrenInitialSize = 2;
private const int MaxByteSequenceLen = 6; // max bytes per character
private const int MaxAttrDuplWalkCount = 250;
private const int MinWhitespaceLookahedCount = 4096;
private const string XmlDeclarationBegining = "" ) );
InitFragmentReader( XmlNodeType.XmlDeclaration, context, true );
}
// Initializes a new instance of the XmlTextReaderImpl class with the specified url and XmlNameTable.
// This constructor is used when creating XmlTextReaderImpl for V1 XmlTextReader
#if !SILVERLIGHT
[ResourceConsumption(ResourceScope.Machine)]
[ResourceExposure(ResourceScope.Machine)]
#endif
public XmlTextReaderImpl( string url ) : this( url, new NameTable() ) {
}
#if !SILVERLIGHT
[ResourceConsumption(ResourceScope.Machine)]
[ResourceExposure(ResourceScope.Machine)]
#endif
public XmlTextReaderImpl( string url, XmlNameTable nt ): this( nt ) {
if ( url == null ) {
throw new ArgumentNullException( "url" );
}
if ( url.Length == 0 ) {
throw new ArgumentException( Res.GetString( Res.Xml_EmptyUrl ), "url" );
}
namespaceManager = new XmlNamespaceManager( nt );
compressedStack = CompressedStack.Capture();
this.url = url;
ps.baseUri = xmlResolver.ResolveUri( null, url );
ps.baseUriStr = ps.baseUri.ToString();
reportedBaseUri = ps.baseUriStr;
parsingFunction = ParsingFunction.OpenUrl;
}
#endif
// Initializes a new instance of the XmlTextReaderImpl class with the specified arguments.
// This constructor is used when creating XmlTextReaderImpl via XmlReader.Create
internal XmlTextReaderImpl( Stream stream, byte[] bytes, int byteCount, XmlReaderSettings settings, Uri baseUri, string baseUriStr,
XmlParserContext context, bool closeInput )
: this( settings.GetXmlResolver(), settings, context ) {
Encoding enc = null;
// get BaseUri and Encoding from XmlParserContext
if ( context != null ) {
if ( context.BaseURI != null && context.BaseURI.Length > 0 &&
!UriEqual( baseUri, baseUriStr, context.BaseURI, settings.GetXmlResolver() ) ) {
if ( baseUriStr.Length > 0 ) {
Throw( Res.Xml_DoubleBaseUri );
}
Debug.Assert( baseUri == null );
baseUriStr = context.BaseURI;
}
enc = context.Encoding;
}
// init ParsingState
InitStreamInput( baseUri, baseUriStr, stream, bytes, byteCount, enc );
this.closeInput = closeInput;
reportedBaseUri = ps.baseUriStr;
reportedEncoding = ps.encoding;
// parse DTD
if ( context != null && context.HasDtdInfo ) {
ProcessDtdFromParserContext(context);
}
}
// Initializes a new instance of the XmlTextReaderImpl class with the specified arguments.
// This constructor is used when creating XmlTextReaderImpl via XmlReader.Create
internal XmlTextReaderImpl( TextReader input, XmlReaderSettings settings, string baseUriStr, XmlParserContext context )
: this( settings.GetXmlResolver(), settings, context ) {
// get BaseUri from XmlParserContext
if ( context != null ) {
Debug.Assert( baseUriStr == string.Empty, "BaseURI can come either from XmlParserContext or from the constructor argument, not from both" );
if ( context.BaseURI != null ) {
baseUriStr = context.BaseURI;
}
}
// init ParsingState
InitTextReaderInput( baseUriStr, input );
this.closeInput = settings.CloseInput;
reportedBaseUri = ps.baseUriStr;
reportedEncoding = ps.encoding;
// parse DTD
if ( context != null && context.HasDtdInfo ) {
ProcessDtdFromParserContext( context);
}
}
#if !SILVERLIGHT
// Initializes a new instance of the XmlTextReaderImpl class for fragment parsing.
// This constructor is used by XmlBinaryReader for nested text XML
internal XmlTextReaderImpl( string xmlFragment, XmlParserContext context, XmlReaderSettings settings )
: this( null, settings, context ) {
InitStringInput( string.Empty, Encoding.Unicode, xmlFragment );
reportedBaseUri = ps.baseUriStr;
reportedEncoding = ps.encoding;
}
#endif
//
// XmlReader members
//
// Returns the current settings of the reader
public override XmlReaderSettings Settings {
get {
XmlReaderSettings settings = new XmlReaderSettings();
if (nameTableFromSettings) {
settings.NameTable = nameTable;
}
switch (fragmentType) {
case XmlNodeType.None: settings.ConformanceLevel = ConformanceLevel.Auto; break;
case XmlNodeType.Element: settings.ConformanceLevel = ConformanceLevel.Fragment; break;
case XmlNodeType.Document: settings.ConformanceLevel = ConformanceLevel.Document; break;
default: Debug.Assert(false); goto case XmlNodeType.None;
}
settings.CheckCharacters = checkCharacters;
settings.LineNumberOffset = lineNumberOffset;
settings.LinePositionOffset = linePositionOffset;
settings.IgnoreWhitespace = (whitespaceHandling == WhitespaceHandling.Significant);
settings.IgnoreProcessingInstructions = ignorePIs;
settings.IgnoreComments = ignoreComments;
settings.DtdProcessing = dtdProcessing;
settings.MaxCharactersInDocument = maxCharactersInDocument;
settings.MaxCharactersFromEntities = maxCharactersFromEntities;
settings.ReadOnly = true;
return settings;
}
}
// Returns the type of the current node.
public override XmlNodeType NodeType {
get {
return curNode.type;
}
}
// Returns the name of the current node, including prefix.
public override string Name {
get {
return curNode.GetNameWPrefix( nameTable );
}
}
// Returns local name of the current node (without prefix)
public override string LocalName {
get {
return curNode.localName;
}
}
// Returns namespace name of the current node.
public override string NamespaceURI {
get {
return curNode.ns;
}
}
// Returns prefix associated with the current node.
public override string Prefix {
get {
return curNode.prefix;
}
}
// Returns the text value of the current node.
public override string Value {
get {
if ( parsingFunction >= ParsingFunction.PartialTextValue ) {
if ( parsingFunction == ParsingFunction.PartialTextValue ) {
FinishPartialValue();
parsingFunction = nextParsingFunction;
}
else {
FinishOtherValueIterator();
}
}
return curNode.StringValue;
}
}
// Returns the depth of the current node in the XML element stack
public override int Depth {
get {
return curNode.depth;
}
}
// Returns the base URI of the current node.
public override string BaseURI {
get {
return reportedBaseUri;
}
}
// Returns true if the current node is an empty element (for example, ).
public override bool IsEmptyElement {
get {
return curNode.IsEmptyElement;
}
}
// Returns true of the current node is a default attribute declared in DTD.
public override bool IsDefault {
get {
return curNode.IsDefaultAttribute;
}
}
#if !SILVERLIGHT
// Returns the quote character used in the current attribute declaration
public override char QuoteChar {
get {
return curNode.type == XmlNodeType.Attribute ? curNode.quoteChar : '"';
}
}
#endif
// Returns the current xml:space scope.
public override XmlSpace XmlSpace {
get {
return xmlContext.xmlSpace;
}
}
// Returns the current xml:lang scope.
public override string XmlLang {
get {
return xmlContext.xmlLang;
}
}
// Returns the current read state of the reader
public override ReadState ReadState {
get {
return readState;
}
}
// Returns true if the reader reached end of the input data
public override bool EOF {
get {
return parsingFunction == ParsingFunction.Eof;
}
}
// Returns the XmlNameTable associated with this XmlReader
public override XmlNameTable NameTable {
get {
return nameTable;
}
}
// Returns true if the XmlReader knows how to resolve general entities
public override bool CanResolveEntity {
get {
return true;
}
}
// Returns the number of attributes on the current node.
public override int AttributeCount {
get {
return attrCount;
}
}
// Returns value of an attribute with the specified Name
public override string GetAttribute( string name ) {
int i;
if ( name.IndexOf( ':' ) == -1 ) {
i = GetIndexOfAttributeWithoutPrefix( name );
}
else {
i = GetIndexOfAttributeWithPrefix( name );
}
return ( i >= 0 ) ? nodes[i].StringValue : null;
}
// Returns value of an attribute with the specified LocalName and NamespaceURI
public override string GetAttribute( string localName, string namespaceURI ) {
namespaceURI = ( namespaceURI == null ) ? string.Empty : nameTable.Get( namespaceURI );
localName = nameTable.Get( localName );
for ( int i = index + 1; i < index + attrCount + 1; i++ ) {
if ( Ref.Equal( nodes[i].localName, localName ) && Ref.Equal( nodes[i].ns, namespaceURI ) ) {
return nodes[i].StringValue;
}
}
return null;
}
// Returns value of an attribute at the specified index (position)
public override string GetAttribute( int i ) {
if ( i < 0 || i >= attrCount ) {
throw new ArgumentOutOfRangeException("i");
}
return nodes[index + i + 1].StringValue;
}
// Moves to an attribute with the specified Name
public override bool MoveToAttribute( string name ) {
int i;
if ( name.IndexOf( ':' ) == -1 ) {
i = GetIndexOfAttributeWithoutPrefix( name );
}
else {
i = GetIndexOfAttributeWithPrefix( name );
}
if ( i >= 0 ) {
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
}
curAttrIndex = i - index - 1;
curNode = nodes[i];
return true;
}
else {
return false;
}
}
// Moves to an attribute with the specified LocalName and NamespceURI
public override bool MoveToAttribute( string localName, string namespaceURI ) {
namespaceURI = ( namespaceURI == null ) ? string.Empty : nameTable.Get( namespaceURI );
localName = nameTable.Get( localName );
for ( int i = index + 1; i < index + attrCount + 1; i++ ) {
if ( Ref.Equal( nodes[i].localName, localName ) &&
Ref.Equal( nodes[i].ns, namespaceURI ) ) {
curAttrIndex = i - index - 1;
curNode = nodes[i];
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
}
return true;
}
}
return false;
}
// Moves to an attribute at the specified index (position)
public override void MoveToAttribute( int i ) {
if ( i < 0 || i >= attrCount ) {
throw new ArgumentOutOfRangeException( "i" );
}
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
}
curAttrIndex = i;
curNode = nodes[index + 1 + curAttrIndex];
}
// Moves to the first attribute of the current node
public override bool MoveToFirstAttribute() {
if ( attrCount == 0 ) {
return false;
}
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
}
curAttrIndex = 0;
curNode = nodes[index + 1];
return true;
}
// Moves to the next attribute of the current node
public override bool MoveToNextAttribute() {
if ( curAttrIndex + 1 < attrCount ) {
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
}
curNode = nodes[ index + 1 + ++curAttrIndex ];
return true;
}
return false;
}
// If on attribute, moves to the element that contains the attribute node
public override bool MoveToElement() {
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
}
else if ( curNode.type != XmlNodeType.Attribute ) {
return false;
}
curAttrIndex = -1;
curNode = nodes[index];
return true;
}
// Reads next node from the input data
public override bool Read() {
for (;;) {
switch ( parsingFunction ) {
case ParsingFunction.ElementContent:
return ParseElementContent();
case ParsingFunction.DocumentContent:
return ParseDocumentContent();
#if !SILVERLIGHT // Needed only for XmlTextReader
case ParsingFunction.OpenUrl:
OpenUrl();
Debug.Assert( nextParsingFunction == ParsingFunction.DocumentContent );
goto case ParsingFunction.SwitchToInteractiveXmlDecl;
#endif
case ParsingFunction.SwitchToInteractive:
Debug.Assert( !ps.appendMode );
readState = ReadState.Interactive;
parsingFunction = nextParsingFunction;
continue;
case ParsingFunction.SwitchToInteractiveXmlDecl:
readState = ReadState.Interactive;
parsingFunction = nextParsingFunction;
if ( ParseXmlDeclaration( false ) ) {
reportedEncoding = ps.encoding;
return true;
}
reportedEncoding = ps.encoding;
continue;
case ParsingFunction.ResetAttributesRootLevel:
ResetAttributes();
curNode = nodes[index];
parsingFunction = ( index == 0 ) ? ParsingFunction.DocumentContent : ParsingFunction.ElementContent;
continue;
case ParsingFunction.MoveToElementContent:
ResetAttributes();
index++;
curNode = AddNode( index, index );
parsingFunction = ParsingFunction.ElementContent;
continue;
case ParsingFunction.PopElementContext:
PopElementContext();
parsingFunction = nextParsingFunction;
Debug.Assert( parsingFunction == ParsingFunction.ElementContent ||
parsingFunction == ParsingFunction.DocumentContent );
continue;
case ParsingFunction.PopEmptyElementContext:
curNode = nodes[index];
Debug.Assert( curNode.type == XmlNodeType.Element );
curNode.IsEmptyElement = false;
ResetAttributes();
PopElementContext();
parsingFunction = nextParsingFunction;
continue;
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
case ParsingFunction.EntityReference:
parsingFunction = nextParsingFunction;
ParseEntityReference();
return true;
case ParsingFunction.ReportEndEntity:
SetupEndEntityNodeInContent();
parsingFunction = nextParsingFunction;
return true;
case ParsingFunction.AfterResolveEntityInContent:
curNode = AddNode( index, index );
reportedEncoding = ps.encoding;
reportedBaseUri = ps.baseUriStr;
parsingFunction = nextParsingFunction;
continue;
case ParsingFunction.AfterResolveEmptyEntityInContent:
curNode = AddNode( index, index );
curNode.SetValueNode( XmlNodeType.Text, string.Empty );
curNode.SetLineInfo( ps.lineNo, ps.LinePos );
reportedEncoding = ps.encoding;
reportedBaseUri = ps.baseUriStr;
parsingFunction = nextParsingFunction;
return true;
#endif
case ParsingFunction.InReadAttributeValue:
FinishAttributeValueIterator();
curNode = nodes[index];
continue;
#if !SILVERLIGHT // Needed only for XmlTextReader (ReadChars, ReadBase64, ReadBinHex)
case ParsingFunction.InIncrementalRead:
FinishIncrementalRead();
return true;
case ParsingFunction.FragmentAttribute:
return ParseFragmentAttribute();
case ParsingFunction.XmlDeclarationFragment:
ParseXmlDeclarationFragment();
parsingFunction = ParsingFunction.GoToEof;
return true;
#endif
case ParsingFunction.GoToEof:
OnEof();
return false;
case ParsingFunction.Error:
case ParsingFunction.Eof:
case ParsingFunction.ReaderClosed:
return false;
case ParsingFunction.NoData:
ThrowWithoutLineInfo( Res.Xml_MissingRoot );
return false;
case ParsingFunction.PartialTextValue:
SkipPartialTextValue();
continue;
case ParsingFunction.InReadValueChunk:
FinishReadValueChunk();
continue;
case ParsingFunction.InReadContentAsBinary:
FinishReadContentAsBinary();
continue;
case ParsingFunction.InReadElementContentAsBinary:
FinishReadElementContentAsBinary();
continue;
default:
Debug.Assert( false );
break;
}
}
}
// Closes the input stream ot TextReader, changes the ReadState to Closed and sets all properties to zero/string.Empty
public override void Close() {
Close( closeInput );
}
// Skips the current node. If on element, skips to the end tag of the element.
public override void Skip() {
if ( readState != ReadState.Interactive )
return;
if ( InAttributeValueIterator ) {
FinishAttributeValueIterator();
curNode = nodes[index];
}
else {
switch ( parsingFunction ) {
case ParsingFunction.InReadAttributeValue:
Debug.Assert( false );
break;
#if !SILVERLIGHT // Needed only for XmlTextReader (ReadChars, ReadBase64, ReadBinHex)
case ParsingFunction.InIncrementalRead:
FinishIncrementalRead();
break;
#endif
case ParsingFunction.PartialTextValue:
SkipPartialTextValue();
break;
case ParsingFunction.InReadValueChunk:
FinishReadValueChunk();
break;
case ParsingFunction.InReadContentAsBinary:
FinishReadContentAsBinary();
break;
case ParsingFunction.InReadElementContentAsBinary:
FinishReadElementContentAsBinary();
break;
}
}
switch ( curNode.type ) {
// skip subtree
case XmlNodeType.Element:
if ( curNode.IsEmptyElement ) {
break;
}
int initialDepth = index;
parsingMode = ParsingMode.SkipContent;
// skip content
while ( outerReader.Read() && index > initialDepth ) ;
Debug.Assert( curNode.type == XmlNodeType.EndElement );
Debug.Assert( parsingFunction != ParsingFunction.Eof );
parsingMode = ParsingMode.Full;
break;
case XmlNodeType.Attribute:
outerReader.MoveToElement();
goto case XmlNodeType.Element;
}
// move to following sibling node
outerReader.Read();
return;
}
// Returns NamespaceURI associated with the specified prefix in the current namespace scope.
public override String LookupNamespace( String prefix ) {
if ( !supportNamespaces ) {
return null;
}
return namespaceManager.LookupNamespace( prefix );
}
// Iterates through the current attribute value's text and entity references chunks.
public override bool ReadAttributeValue() {
if ( parsingFunction != ParsingFunction.InReadAttributeValue ) {
if ( curNode.type != XmlNodeType.Attribute ) {
return false;
}
if ( readState != ReadState.Interactive || curAttrIndex < 0 ) {
return false;
}
if ( parsingFunction == ParsingFunction.InReadValueChunk ) {
FinishReadValueChunk();
}
if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
FinishReadContentAsBinary();
}
#if SILVERLIGHT
NodeData simpleValueNode = AddNode( index + attrCount + 1, curNode.depth + 1 );
simpleValueNode.SetValueNode( XmlNodeType.Text, curNode.StringValue );
simpleValueNode.lineInfo = curNode.lineInfo2;
simpleValueNode.depth = curNode.depth + 1;
curNode = simpleValueNode;
nextParsingFunction = parsingFunction;
parsingFunction = ParsingFunction.InReadAttributeValue;
#else
if ( curNode.nextAttrValueChunk == null || entityHandling == EntityHandling.ExpandEntities ) {
NodeData simpleValueNode = AddNode( index + attrCount + 1, curNode.depth + 1 );
simpleValueNode.SetValueNode( XmlNodeType.Text, curNode.StringValue );
simpleValueNode.lineInfo = curNode.lineInfo2;
simpleValueNode.depth = curNode.depth + 1;
curNode = simpleValueNode;
simpleValueNode.nextAttrValueChunk = null;
}
else {
curNode = curNode.nextAttrValueChunk;
// Place the current node at nodes[index + attrCount + 1]. If the node type
// is be EntityReference and user calls ResolveEntity, the associated EndEntity
// node will be constructed from the information stored there.
// This will initialize the (index + attrCount + 1) place in nodes array
AddNode( index + attrCount + 1, index + 2 );
nodes[index + attrCount + 1] = curNode;
fullAttrCleanup = true;
}
nextParsingFunction = parsingFunction;
parsingFunction = ParsingFunction.InReadAttributeValue;
attributeValueBaseEntityId = ps.entityId;
#endif
return true;
}
else {
#if SILVERLIGHT
return false;
#else
if ( ps.entityId == attributeValueBaseEntityId ) {
if ( curNode.nextAttrValueChunk != null ) {
curNode = curNode.nextAttrValueChunk;
nodes[index + attrCount + 1] = curNode; // if curNode == EntityReference node, it will be picked from here by SetupEndEntityNodeInAttribute
return true;
}
return false;
}
else {
// expanded entity in attribute value
return ParseAttributeValueChunk();
}
#endif
}
}
// Resolves the current entity reference node
public override void ResolveEntity() {
#if SILVERLIGHT // entities are always resolved V2 XmlReader that is Silverlight
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
#else
if ( curNode.type != XmlNodeType.EntityReference ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
}
Debug.Assert( parsingMode == ParsingMode.Full );
// entity in attribute value
if ( parsingFunction == ParsingFunction.InReadAttributeValue ||
parsingFunction == ParsingFunction.FragmentAttribute ) {
switch ( HandleGeneralEntityReference( curNode.localName, true, true, curNode.LinePos ) ) {
case EntityType.ExpandedInAttribute:
case EntityType.Expanded:
if ( ps.charsUsed - ps.charPos == 0 ) { // entity value == ""
emptyEntityInAttributeResolved = true;
}
break;
case EntityType.FakeExpanded:
emptyEntityInAttributeResolved = true;
break;
default:
Debug.Assert( false );
throw new XmlException( Res.Xml_InternalError, string.Empty );
}
}
// entity in element content
else {
switch ( HandleGeneralEntityReference( curNode.localName, false, true, curNode.LinePos ) ) {
case EntityType.ExpandedInAttribute:
case EntityType.Expanded:
nextParsingFunction = parsingFunction;
if ( ps.charsUsed - ps.charPos == 0 && !ps.entity.IsExternal ) { // empty internal entity value
parsingFunction = ParsingFunction.AfterResolveEmptyEntityInContent;
}
else {
parsingFunction = ParsingFunction.AfterResolveEntityInContent;
}
break;
case EntityType.FakeExpanded:
nextParsingFunction = parsingFunction;
parsingFunction = ParsingFunction.AfterResolveEmptyEntityInContent;
break;
default:
Debug.Assert( false );
throw new XmlException( Res.Xml_InternalError, string.Empty );
}
}
ps.entityResolvedManually = true;
index++;
#endif
}
#if !SILVERLIGHT // Needed only for XmlTextReader or XmlValidatingReader
internal XmlReader OuterReader {
get {
return outerReader;
}
set {
Debug.Assert( value is XmlTextReader );
outerReader = value;
}
}
internal void MoveOffEntityReference() {
if ( outerReader.NodeType == XmlNodeType.EntityReference &&
parsingFunction == ParsingFunction.AfterResolveEntityInContent ) {
if ( !outerReader.Read() ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
}
}
}
public override string ReadString() {
Debug.Assert( outerReader is XmlTextReaderImpl );
MoveOffEntityReference();
return base.ReadString();
}
#endif
public override bool CanReadBinaryContent {
get {
return true;
}
}
// Reads and concatenates content nodes, base64-decodes the results and copies the decoded bytes into the provided buffer
public override int ReadContentAsBase64( byte[] buffer, int index, int count ) {
// check arguments
if ( buffer == null ) {
throw new ArgumentNullException( "buffer" );
}
if ( count < 0 ) {
throw new ArgumentOutOfRangeException( "count" );
}
if ( index < 0 ) {
throw new ArgumentOutOfRangeException( "index" );
}
if ( buffer.Length - index < count ) {
throw new ArgumentOutOfRangeException( "count" );
}
// if not the first call to ReadContentAsBase64
if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
// and if we have a correct decoder
if ( incReadDecoder == base64Decoder ) {
// read more binary data
return ReadContentAsBinary( buffer, index, count );
}
}
// first call of ReadContentAsBase64 -> initialize (move to first text child (for elements) and initialize incremental read state)
else {
if ( readState != ReadState.Interactive ) {
return 0;
}
if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
}
if ( !XmlReader.CanReadContentAs( curNode.type ) ) {
throw CreateReadContentAsException( "ReadContentAsBase64" );
}
if ( !InitReadContentAsBinary() ) {
return 0;
}
}
// setup base64 decoder
InitBase64Decoder();
// read binary data
return ReadContentAsBinary( buffer, index, count );
}
// Reads and concatenates content nodes, binhex-decodes the results and copies the decoded bytes into the provided buffer
public override int ReadContentAsBinHex( byte[] buffer, int index, int count ) {
// check arguments
if ( buffer == null ) {
throw new ArgumentNullException( "buffer" );
}
if ( count < 0 ) {
throw new ArgumentOutOfRangeException( "count" );
}
if ( index < 0 ) {
throw new ArgumentOutOfRangeException( "index" );
}
if ( buffer.Length - index < count ) {
throw new ArgumentOutOfRangeException( "count" );
}
// if not the first call to ReadContentAsBinHex
if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
// and if we have a correct decoder
if ( incReadDecoder == binHexDecoder ) {
// read more binary data
return ReadContentAsBinary( buffer, index, count );
}
}
// first call of ReadContentAsBinHex -> initialize (move to first text child (for elements) and initialize incremental read state)
else {
if ( readState != ReadState.Interactive ) {
return 0;
}
if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
}
if ( !XmlReader.CanReadContentAs( curNode.type ) ) {
throw CreateReadContentAsException( "ReadContentAsBinHex" );
}
if ( !InitReadContentAsBinary() ) {
return 0;
}
}
// setup binhex decoder (when in first ReadContentAsBinHex call or when mixed with ReadContentAsBase64)
InitBinHexDecoder();
// read binary data
return ReadContentAsBinary( buffer, index, count );
}
// Reads and concatenates content of an element, base64-decodes the results and copies the decoded bytes into the provided buffer
public override int ReadElementContentAsBase64( byte[] buffer, int index, int count ) {
// check arguments
if ( buffer == null ) {
throw new ArgumentNullException( "buffer" );
}
if ( count < 0 ) {
throw new ArgumentOutOfRangeException( "count" );
}
if ( index < 0 ) {
throw new ArgumentOutOfRangeException( "index" );
}
if ( buffer.Length - index < count ) {
throw new ArgumentOutOfRangeException( "count" );
}
// if not the first call to ReadContentAsBase64
if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
// and if we have a correct decoder
if ( incReadDecoder == base64Decoder ) {
// read more binary data
return ReadElementContentAsBinary( buffer, index, count );
}
}
// first call of ReadElementContentAsBase64 -> initialize
else {
if ( readState != ReadState.Interactive ) {
return 0;
}
if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
}
if ( curNode.type != XmlNodeType.Element ) {
throw CreateReadElementContentAsException( "ReadElementContentAsBinHex" );
}
if ( !InitReadElementContentAsBinary() ) {
return 0;
}
}
// setup base64 decoder
InitBase64Decoder();
// read binary data
return ReadElementContentAsBinary( buffer, index, count );
}
// Reads and concatenates content of an element, binhex-decodes the results and copies the decoded bytes into the provided buffer
public override int ReadElementContentAsBinHex( byte[] buffer, int index, int count ) {
// check arguments
if ( buffer == null ) {
throw new ArgumentNullException( "buffer" );
}
if ( count < 0 ) {
throw new ArgumentOutOfRangeException( "count" );
}
if ( index < 0 ) {
throw new ArgumentOutOfRangeException( "index" );
}
if ( buffer.Length - index < count ) {
throw new ArgumentOutOfRangeException( "count" );
}
// if not the first call to ReadContentAsBinHex
if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
// and if we have a correct decoder
if ( incReadDecoder == binHexDecoder ) {
// read more binary data
return ReadElementContentAsBinary( buffer, index, count );
}
}
// first call of ReadContentAsBinHex -> initialize
else {
if ( readState != ReadState.Interactive ) {
return 0;
}
if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
}
if ( curNode.type != XmlNodeType.Element ) {
throw CreateReadElementContentAsException( "ReadElementContentAsBinHex" );
}
if ( !InitReadElementContentAsBinary() ) {
return 0;
}
}
// setup binhex decoder (when in first ReadContentAsBinHex call or when mixed with ReadContentAsBase64)
InitBinHexDecoder();
// read binary data
return ReadElementContentAsBinary( buffer, index, count );
}
// Returns true if ReadValue is supported
public override bool CanReadValueChunk {
get {
return true;
}
}
// Iterates over Value property and copies it into the provided buffer
public override int ReadValueChunk( char[] buffer, int index, int count ) {
// throw on elements
if ( !XmlReader.HasValueInternal( curNode.type ) ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidReadValueChunk, curNode.type ) ) ;
}
// check arguments
if ( buffer == null ) {
throw new ArgumentNullException( "buffer" );
}
if ( count < 0 ) {
throw new ArgumentOutOfRangeException( "count" );
}
if ( index < 0 ) {
throw new ArgumentOutOfRangeException( "index" );
}
if ( buffer.Length - index < count ) {
throw new ArgumentOutOfRangeException( "count" );
}
// first call of ReadValueChunk -> initialize incremental read state
if ( parsingFunction != ParsingFunction.InReadValueChunk ) {
if ( readState != ReadState.Interactive ) {
return 0;
}
if ( parsingFunction == ParsingFunction.PartialTextValue ) {
incReadState = IncrementalReadState.ReadValueChunk_OnPartialValue;
}
else {
incReadState = IncrementalReadState.ReadValueChunk_OnCachedValue;
nextNextParsingFunction = nextParsingFunction;
nextParsingFunction = parsingFunction;
}
parsingFunction = ParsingFunction.InReadValueChunk;
readValueOffset = 0;
}
if ( count == 0 ) {
return 0;
}
// read what is already cached in curNode
int readCount = 0;
int read = curNode.CopyTo( readValueOffset, buffer, index + readCount, count - readCount );
readCount += read;
readValueOffset += read;
if ( readCount == count ) {
// take care of surrogate pairs spanning between buffers
char ch = buffer[index + count - 1];
if ( XmlCharType.IsHighSurrogate(ch) ) {
readCount--;
readValueOffset--;
if ( readCount == 0 ) {
Throw( Res.Xml_NotEnoughSpaceForSurrogatePair );
}
}
return readCount;
}
// if on partial value, read the rest of it
if ( incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue ) {
curNode.SetValue( string.Empty );
// read next chunk of text
bool endOfValue = false;
int startPos = 0;
int endPos = 0;
while ( readCount < count && !endOfValue ) {
int orChars = 0;
endOfValue = ParseText( out startPos, out endPos, ref orChars );
int copyCount = count - readCount;
if ( copyCount > endPos - startPos ) {
copyCount = endPos - startPos;
}
BlockCopyChars( ps.chars, startPos, buffer, ( index + readCount ), copyCount );
readCount += copyCount;
startPos += copyCount;
}
incReadState = endOfValue ? IncrementalReadState.ReadValueChunk_OnCachedValue : IncrementalReadState.ReadValueChunk_OnPartialValue;
if ( readCount == count ) {
char ch = buffer[index + count - 1];
if ( XmlCharType.IsHighSurrogate(ch) ) {
readCount--;
startPos--;
if ( readCount == 0 ) {
Throw( Res.Xml_NotEnoughSpaceForSurrogatePair );
}
}
}
readValueOffset = 0;
curNode.SetValue( ps.chars, startPos, endPos - startPos );
}
return readCount;
}
//
// IXmlLineInfo members
//
public bool HasLineInfo() {
return true;
}
// Returns the line number of the current node
public int LineNumber {
get {
return curNode.LineNo;
}
}
// Returns the line position of the current node
public int LinePosition {
get {
return curNode.LinePos;
}
}
//
// IXmlNamespaceResolver members
//
IDictionary IXmlNamespaceResolver.GetNamespacesInScope( XmlNamespaceScope scope ) {
return this.GetNamespacesInScope( scope );
}
string IXmlNamespaceResolver.LookupNamespace(string prefix) {
return this.LookupNamespace( prefix );
}
string IXmlNamespaceResolver.LookupPrefix( string namespaceName ) {
return this.LookupPrefix( namespaceName );
}
// Internal IXmlNamespaceResolver methods
internal IDictionary GetNamespacesInScope( XmlNamespaceScope scope ) {
return namespaceManager.GetNamespacesInScope( scope );
}
// NOTE: there already is virtual method for "string LookupNamespace(string prefix)"
internal string LookupPrefix( string namespaceName ) {
return namespaceManager.LookupPrefix( namespaceName );
}
//
// XmlTextReader members
//
#if !SILVERLIGHT // Needed only for XmlTextReader
// Disables or enables support of W3C XML 1.0 Namespaces
internal bool Namespaces {
get {
return supportNamespaces;
}
set {
if ( readState != ReadState.Initial ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
}
supportNamespaces = value;
if ( value ) {
if ( namespaceManager is NoNamespaceManager ) {
if ( fragment && fragmentParserContext != null && fragmentParserContext.NamespaceManager != null ) {
namespaceManager = fragmentParserContext.NamespaceManager;
}
else {
namespaceManager = new XmlNamespaceManager( nameTable );
}
}
xmlContext.defaultNamespace = namespaceManager.LookupNamespace( string.Empty );
}
else {
if ( !( namespaceManager is NoNamespaceManager ) ) {
namespaceManager = new NoNamespaceManager();
}
xmlContext.defaultNamespace = string.Empty;
}
}
}
// Enables or disables XML 1.0 normalization (incl. end-of-line normalization and normalization of attributes)
internal bool Normalization {
get {
Debug.Assert( v1Compat, "XmlTextReaderImpl.Normalization property cannot be accessed on reader created via XmlReader.Create." );
return normalize;
}
set {
Debug.Assert( v1Compat, "XmlTextReaderImpl.Normalization property cannot be changed on reader created via XmlReader.Create." );
if ( readState == ReadState.Closed ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
}
normalize = value;
if ( ps.entity == null || ps.entity.IsExternal ) {
ps.eolNormalized = !value;
}
}
}
// Returns the Encoding of the XML document
internal Encoding Encoding {
get {
return ( readState == ReadState.Interactive ) ? reportedEncoding : null;
}
}
// Spefifies whitespace handling of the XML document, i.e. whether return all namespaces, only significant ones or none
internal WhitespaceHandling WhitespaceHandling {
get {
Debug.Assert( v1Compat, "XmlTextReaderImpl.WhitespaceHandling property cannot be accessed on reader created via XmlReader.Create." );
return whitespaceHandling;
}
set {
Debug.Assert( v1Compat, "XmlTextReaderImpl.WhitespaceHandling property cannot be changed on reader created via XmlReader.Create." );
if ( readState == ReadState.Closed ) {
throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
}
if ( (uint)value > (uint)WhitespaceHandling.None ) {
throw new XmlException( Res.Xml_WhitespaceHandling, string.Empty );
}
whitespaceHandling = value;
}
}
// Specifies how the DTD is processed in the XML document.
internal DtdProcessing DtdProcessing {
get {
Debug.Assert( v1Compat, "XmlTextReaderImpl.DtdProcessing property cannot be accessed on reader created via XmlReader.Create." );
return dtdProcessing;
}
set {
Debug.Assert( v1Compat, "XmlTextReaderImpl.DtdProcessing property cannot be changed on reader created via XmlReader.Create." );
if ((uint)value > (uint)DtdProcessing.Parse) {
throw new ArgumentOutOfRangeException("value");
}
dtdProcessing = value;
}
}
// Spefifies whether general entities should be automatically expanded or not
internal EntityHandling EntityHandling {
get {
return entityHandling;
}
set {
if ( value != EntityHandling.ExpandEntities && value != EntityHandling.ExpandCharEntities ) {
throw new XmlException( Res.Xml_EntityHandling, string.Empty );
}
entityHandling = value;
}
}
// Specifies XmlResolver used for opening the XML document and other external references
internal XmlResolver XmlResolver {
set {
xmlResolver = value;
// invalidate all baseUris on the stack
ps.baseUri = null;
for ( int i = 0; i <= parsingStatesStackTop; i++ ) {
parsingStatesStack[i].baseUri = null;
}
}
}
// Reset the state of the reader so the reader is ready to parse another XML document from the same stream.
internal void ResetState() {
Debug.Assert( v1Compat, "XmlTextReaderImpl.ResetState cannot be called on reader created via XmlReader.Create." );
if ( fragment ) {
Throw( new InvalidOperationException( Res.GetString( Res.Xml_InvalidResetStateCall ) ) );
}
if ( readState == ReadState.Initial ) {
return;
}
// Clear
ResetAttributes();
while ( namespaceManager.PopScope() );
while ( InEntity ) {
HandleEntityEnd( true );
}
// Init
readState = ReadState.Initial;
parsingFunction = ParsingFunction.SwitchToInteractiveXmlDecl;
nextParsingFunction = ParsingFunction.DocumentContent;
curNode = nodes[0];
curNode.Clear( XmlNodeType.None );
curNode.SetLineInfo( 0, 0 );
index = 0;
rootElementParsed = false;
charactersInDocument = 0;
charactersFromEntities = 0;
afterResetState = true;
}
// returns the remaining unparsed data as TextReader
internal TextReader GetRemainder() {
Debug.Assert( v1Compat, "XmlTextReaderImpl.GetRemainder cannot be called on reader created via XmlReader.Create." );
Debug.Assert( stringBuilder.Length == 0 );
switch ( parsingFunction ) {
case ParsingFunction.Eof:
case ParsingFunction.ReaderClosed:
return new StringReader( string.Empty );
case ParsingFunction.OpenUrl:
OpenUrl();
break;
case ParsingFunction.InIncrementalRead:
if ( !InEntity ) {
stringBuilder.Append( ps.chars, incReadLeftStartPos, incReadLeftEndPos - incReadLeftStartPos );
}
break;
}
while ( InEntity ) {
HandleEntityEnd( true );
}
ps.appendMode = false;
do {
stringBuilder.Append( ps.chars, ps.charPos, ps.charsUsed - ps.charPos );
ps.charPos = ps.charsUsed;
} while ( ReadData() != 0 );
OnEof();
string remainer = stringBuilder.ToString();
stringBuilder.Length = 0;
return new StringReader( remainer );
}
// Reads the contents of an element including markup into a character buffer. Wellformedness checks are limited.
// This method is designed to read large streams of embedded text by calling it successively.
internal int ReadChars( char[] buffer, int index, int count ) {
Debug.Assert( v1Compat, "XmlTextReaderImpl.ReadChars cannot be called on reader created via XmlReader.Create." );
Debug.Assert( outerReader is XmlTextReader );
if ( parsingFunction == ParsingFunction.InIncrementalRead ) {
if ( incReadDecoder != readCharsDecoder ) { // mixing ReadChars with ReadBase64 or ReadBinHex
if ( readCharsDecoder == null ) {
readCharsDecoder = new IncrementalReadCharsDecoder();
}
readCharsDecoder.Reset();
incReadDecoder = readCharsDecoder;
}
return IncrementalRead( buffer, index, count );
}
else {
if ( curNode.type != XmlNodeType.Element) {
return 0;
}
if ( curNode.IsEmptyElement ) {
outerReader.Read();
return 0;
}
if ( readCharsDecoder == null ) {
readCharsDecoder = new IncrementalReadCharsDecoder();
}
InitIncrementalRead( readCharsDecoder );
return IncrementalRead( buffer, index, count );
}
}
// Reads the contents of an element including markup and base64-decodes it into a byte buffer. Wellformedness checks are limited.
// This method is designed to read base64-encoded large streams of bytes by calling it successively.
internal int ReadBase64( byte[] array, int offset, int len ) {
Debug.Assert( v1Compat, "XmlTextReaderImpl.ReadBase64 cannot be called on reader created via XmlReader.Create." );
Debug.Assert( outerReader is XmlTextReader );
if ( parsingFunction == ParsingFunction.InIncrementalRead ) {
if ( incReadDecoder != base64Decoder ) { // mixing ReadBase64 with ReadChars or ReadBinHex
InitBase64Decoder();
}
return IncrementalRead( array, offset, len );
}
else {
if ( curNode.type != XmlNodeType.Element) {
return 0;
}
if ( curNode.IsEmptyElement ) {
outerReader.Read();
return 0;
}
if ( base64Decoder == null ) {
base64Decoder = new Base64Decoder();
}
InitIncrementalRead( base64Decoder );
return IncrementalRead( array, offset, len );
}
}
// Reads the contents of an element including markup and binhex-decodes it into a byte buffer. Wellformedness checks are limited.
// This method is designed to read binhex-encoded large streams of bytes by calling it successively.
internal int ReadBinHex( byte[] array, int offset, int len ) {
Debug.Assert( v1Compat, "XmlTextReaderImpl.ReadBinHex cannot be called on reader created via XmlReader.Create." );
Debug.Assert( outerReader is XmlTextReader );
if ( parsingFunction == ParsingFunction.InIncrementalRead ) {
if ( incReadDecoder != binHexDecoder ) { // mixing ReadBinHex with ReadChars or ReadBase64
InitBinHexDecoder();
}
return IncrementalRead( array, offset, len );
}
else {
if ( curNode.type != XmlNodeType.Element) {
return 0;
}
if ( curNode.IsEmptyElement ) {
outerReader.Read();
return 0;
}
if ( binHexDecoder == null ) {
binHexDecoder = new BinHexDecoder();
}
InitIncrementalRead( binHexDecoder );
return IncrementalRead( array, offset, len );
}
}
#endif
//
// Helpers for DtdParserProxy
//
internal XmlNameTable DtdParserProxy_NameTable {
get {
return nameTable;
}
}
internal IXmlNamespaceResolver DtdParserProxy_NamespaceResolver {
get {
return namespaceManager;
}
}
internal bool DtdParserProxy_DtdValidation {
get {
#if SILVERLIGHT
return false;
#else
return DtdValidation;
#endif
}
}
internal bool DtdParserProxy_Normalization {
get {
return normalize;
}
}
internal bool DtdParserProxy_Namespaces {
get {
return supportNamespaces;
}
}
internal bool DtdParserProxy_V1CompatibilityMode {
get {
return v1Compat;
}
}
internal Uri DtdParserProxy_BaseUri {
// SxS: ps.baseUri may be initialized in the constructor (public XmlTextReaderImpl( string url, XmlNameTable nt )) based on
// url provided by the user. Here the property returns ps.BaseUri - so it may expose a path.
#if !SILVERLIGHT
[ResourceConsumption(ResourceScope.Machine)]
[ResourceExposure(ResourceScope.Machine)]
#endif
get {
if ( ps.baseUriStr.Length > 0 && ps.baseUri == null && xmlResolver != null ) {
ps.baseUri = xmlResolver.ResolveUri( null, ps.baseUriStr );
}
return ps.baseUri;
}
}
internal bool DtdParserProxy_IsEof {
get {
return ps.isEof;
}
}
internal char[] DtdParserProxy_ParsingBuffer {
get {
return ps.chars;
}
}
internal int DtdParserProxy_ParsingBufferLength {
get {
return ps.charsUsed;
}
}
internal int DtdParserProxy_CurrentPosition {
get {
return ps.charPos;
}
set {
Debug.Assert( value >= 0 && value <= ps.charsUsed );
ps.charPos = value;
}
}
internal int DtdParserProxy_EntityStackLength {
get {
return parsingStatesStackTop + 1;
}
}
internal bool DtdParserProxy_IsEntityEolNormalized {
get {
return ps.eolNormalized;
}
}
#if !SILVERLIGHT
internal IValidationEventHandling DtdParserProxy_ValidationEventHandling {
get {
return validationEventHandling;
}
set {
validationEventHandling = value;
}
}
#endif
internal void DtdParserProxy_OnNewLine( int pos ) {
this.OnNewLine( pos );
}
internal int DtdParserProxy_LineNo {
get {
return ps.LineNo;
}
}
internal int DtdParserProxy_LineStartPosition {
get {
return ps.lineStartPos;
}
}
internal int DtdParserProxy_ReadData() {
return this.ReadData();
}
internal int DtdParserProxy_ParseNumericCharRef( BufferBuilder internalSubsetBuilder ) {
EntityType entType;
return this.ParseNumericCharRef( true, internalSubsetBuilder, out entType );
}
internal int DtdParserProxy_ParseNamedCharRef( bool expand, BufferBuilder internalSubsetBuilder ) {
return this.ParseNamedCharRef( expand, internalSubsetBuilder );
}
internal void DtdParserProxy_ParsePI( BufferBuilder sb ) {
if ( sb == null ) {
ParsingMode pm = parsingMode;
parsingMode = ParsingMode.SkipNode;
ParsePI( null );
parsingMode = pm;
}
else {
ParsePI( sb );
}
}
internal void DtdParserProxy_ParseComment( BufferBuilder sb ) {
Debug.Assert( parsingMode == ParsingMode.Full );
try {
if ( sb == null ) {
ParsingMode savedParsingMode = parsingMode;
parsingMode = ParsingMode.SkipNode;
ParseCDataOrComment( XmlNodeType.Comment );
parsingMode = savedParsingMode;
}
else {
NodeData originalCurNode = curNode;
curNode = AddNode( index + attrCount + 1, index );
ParseCDataOrComment( XmlNodeType.Comment );
curNode.CopyTo( 0, sb );
curNode = originalCurNode;
}
}
catch ( XmlException e ) {
#if !SILVERLIGHT
if ( e.ResString == Res.Xml_UnexpectedEOF && ps.entity != null ) {
SendValidationEvent( XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, null, ps.LineNo, ps.LinePos );
}
else {
throw;
}
#else
throw e;
#endif
}
}
internal bool DtdParserProxy_PushEntity( IDtdEntityInfo entity, out int entityId ) {
bool retValue;
if ( entity.IsExternal ) {
if ( xmlResolver == null ) {
entityId = -1;
return false;
}
retValue = PushExternalEntity( entity );
}
else {
PushInternalEntity( entity );
retValue = true;
}
entityId = ps.entityId;
return retValue;
}
internal bool DtdParserProxy_PopEntity( out IDtdEntityInfo oldEntity, out int newEntityId ) {
if ( parsingStatesStackTop == -1 ) {
oldEntity = null;
newEntityId = -1;
return false;
}
oldEntity = ps.entity;
PopEntity();
newEntityId = ps.entityId;
return true;
}
// SxS: The caller did not provide any SxS sensitive name or resource. No resource is being exposed either.
// It is OK to suppress SxS warning.
#if !SILVERLIGHT
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[ResourceExposure(ResourceScope.None)]
#endif
internal bool DtdParserProxy_PushExternalSubset( string systemId, string publicId ) {
Debug.Assert( parsingStatesStackTop == -1 );
Debug.Assert( ( systemId != null && systemId.Length > 0 ) || ( publicId != null && publicId.Length > 0 ) );
if ( xmlResolver == null ) {
return false;
}
PushExternalEntityOrSubset( publicId, systemId, ps.baseUriStr, ref ps.baseUri, null );
ps.entity = null;
ps.entityId = 0;
Debug.Assert( ps.appendMode );
int initialPos = ps.charPos;
if ( v1Compat ) {
EatWhitespaces( null );
}
if ( !ParseXmlDeclaration( true ) ) {
ps.charPos = initialPos;
}
return true;
}
internal void DtdParserProxy_PushInternalDtd( string baseUri, string internalDtd ) {
Debug.Assert( parsingStatesStackTop == -1 );
PushParsingState();
RegisterConsumedCharacters(internalDtd.Length, false);
InitStringInput( baseUri, Encoding.Unicode, internalDtd );
ps.entity = null;
ps.entityId = 0;
ps.eolNormalized = false;
}
internal void DtdParserProxy_Throw( Exception e ) {
this.Throw( e );
}
internal void DtdParserProxy_OnSystemId( string systemId, LineInfo keywordLineInfo, LineInfo systemLiteralLineInfo ) {
NodeData attr = AddAttributeNoChecks( "SYSTEM", index + 1 );
attr.SetValue( systemId );
attr.lineInfo = keywordLineInfo;
attr.lineInfo2 = systemLiteralLineInfo;
}
internal void DtdParserProxy_OnPublicId( string publicId, LineInfo keywordLineInfo, LineInfo publicLiteralLineInfo ) {
NodeData attr = AddAttributeNoChecks( "PUBLIC", index + 1 );
attr.SetValue( publicId );
attr.lineInfo = keywordLineInfo;
attr.lineInfo2 = publicLiteralLineInfo;
}
//
// Throw methods: Sets the reader current position to pos, sets the error state and throws exception
//
void Throw( int pos, string res, string arg ) {
ps.charPos = pos;
Throw( res, arg );
}
void Throw( int pos, string res, string[] args ) {
ps.charPos = pos;
Throw( res, args );
}
void Throw( int pos, string res ) {
ps.charPos = pos;
Throw( res, string.Empty );
}
void Throw( string res ) {
Throw( res, string.Empty );
}
void Throw( string res, int lineNo, int linePos ) {
Throw( new XmlException( res, string.Empty, lineNo, linePos, ps.baseUriStr ) );
}
void Throw( string res, string arg ) {
Throw( new XmlException( res, arg, ps.LineNo, ps.LinePos, ps.baseUriStr ) );
}
void Throw( string res, string arg, int lineNo, int linePos ) {
Throw( new XmlException( res, arg, lineNo, linePos, ps.baseUriStr ) );
}
void Throw( string res, string[] args ) {
Throw( new XmlException( res, args, ps.LineNo, ps.LinePos, ps.baseUriStr ) );
}
void Throw( string res, string arg, Exception innerException ) {
Throw( res, new string[] { arg }, innerException );
}
void Throw( string res, string[] args, Exception innerException ) {
Throw( new XmlException( res, args, innerException, ps.LineNo, ps.LinePos, ps.baseUriStr ) );
}
void Throw( Exception e ) {
SetErrorState();
XmlException xmlEx = e as XmlException;
if ( xmlEx != null ) {
curNode.SetLineInfo( xmlEx.LineNumber, xmlEx.LinePosition );
}
throw e;
}
void ReThrow( Exception e, int lineNo, int linePos ) {
Throw( new XmlException( e.Message, (Exception)null, lineNo, linePos, ps.baseUriStr ) );
}
void ThrowWithoutLineInfo( string res ) {
Throw( new XmlException( res, string.Empty, ps.baseUriStr ) );
}
void ThrowWithoutLineInfo( string res, string arg ) {
Throw( new XmlException( res, arg, ps.baseUriStr ) );
}
void ThrowWithoutLineInfo( string res, string[] args, Exception innerException ) {
Throw( new XmlException( res, args, innerException, 0, 0, ps.baseUriStr ) );
}
void ThrowInvalidChar( char[] data, int length, int invCharPos ) {
Throw( invCharPos, Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs( data, length, invCharPos ) );
}
private void SetErrorState() {
parsingFunction = ParsingFunction.Error;
readState = ReadState.Error;
}
#if !SILVERLIGHT
void SendValidationEvent( XmlSeverityType severity, string code, string arg, int lineNo, int linePos ) {
SendValidationEvent( severity, new XmlSchemaException( code, arg, ps.baseUriStr, lineNo, linePos ) );
}
void SendValidationEvent(XmlSeverityType severity, XmlSchemaException exception) {
if (validationEventHandling != null) {
validationEventHandling.SendEvent(exception, severity);
}
}
#endif
//
// Private implementation methods & properties
//
private bool InAttributeValueIterator {
[System.Runtime.TargetedPatchingOptOutAttribute("Performance critical to inline across NGen image boundaries")]
get {
return attrCount > 0 && parsingFunction >= ParsingFunction.InReadAttributeValue;
}
}
private void FinishAttributeValueIterator() {
Debug.Assert( InAttributeValueIterator );
if ( parsingFunction == ParsingFunction.InReadValueChunk ) {
FinishReadValueChunk();
}
else if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
FinishReadContentAsBinary();
}
if ( parsingFunction == ParsingFunction.InReadAttributeValue ) {
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
while ( ps.entityId != attributeValueBaseEntityId ) {
HandleEntityEnd( false );
}
emptyEntityInAttributeResolved = false;
#endif
parsingFunction = nextParsingFunction;
nextParsingFunction = ( index > 0 ) ? ParsingFunction.ElementContent : ParsingFunction.DocumentContent;
}
}
#if !SILVERLIGHT
private bool DtdValidation {
get {
return validationEventHandling != null;
}
}
private void InitStreamInput( Stream stream, Encoding encoding ) {
InitStreamInput( null, string.Empty, stream, null, 0, encoding );
}
private void InitStreamInput( string baseUriStr, Stream stream, Encoding encoding ) {
Debug.Assert( baseUriStr != null );
InitStreamInput( null, baseUriStr, stream, null, 0, encoding );
}
#endif
private void InitStreamInput( Uri baseUri, Stream stream, Encoding encoding ) {
Debug.Assert( baseUri != null );
InitStreamInput( baseUri, baseUri.ToString(), stream, null, 0, encoding );
}
#if !SILVERLIGHT
private void InitStreamInput( Uri baseUri, string baseUriStr, Stream stream, Encoding encoding ) {
InitStreamInput( baseUri, baseUriStr, stream, null, 0, encoding );
}
#endif
private void InitStreamInput( Uri baseUri, string baseUriStr, Stream stream, byte[] bytes, int byteCount, Encoding encoding ) {
Debug.Assert( ps.charPos == 0 && ps.charsUsed == 0 && ps.textReader == null );
Debug.Assert( baseUriStr != null );
Debug.Assert( baseUri == null || ( baseUri.ToString().Equals( baseUriStr ) ) );
ps.stream = stream;
ps.baseUri = baseUri;
ps.baseUriStr = baseUriStr;
// take over the byte buffer allocated in XmlReader.Create, if available
int bufferSize;
if ( bytes != null ) {
ps.bytes = bytes;
ps.bytesUsed = byteCount;
bufferSize = ps.bytes.Length;
}
else {
// allocate the byte buffer
bufferSize = XmlReader.CalcBufferSize( stream );
if ( ps.bytes == null || ps.bytes.Length < bufferSize ) {
ps.bytes = new byte[ bufferSize ];
}
}
// allocate char buffer
if ( ps.chars == null || ps.chars.Length < bufferSize + 1 ) {
ps.chars = new char[ bufferSize + 1 ];
}
// make sure we have at least 4 bytes to detect the encoding (no preamble of System.Text supported encoding is longer than 4 bytes)
ps.bytePos = 0;
while ( ps.bytesUsed < 4 && ps.bytes.Length - ps.bytesUsed > 0 ) {
int read = stream.Read( ps.bytes, ps.bytesUsed, ps.bytes.Length - ps.bytesUsed );
if ( read == 0 ) {
ps.isStreamEof = true;
break;
}
ps.bytesUsed += read;
}
// detect & setup encoding
if ( encoding == null ) {
encoding = DetectEncoding();
}
SetupEncoding( encoding );
// eat preamble
byte[] preamble = ps.encoding.GetPreamble();
int preambleLen = preamble.Length;
int i;
for ( i = 0; i < preambleLen && i < ps.bytesUsed; i++ ) {
if ( ps.bytes[i] != preamble[i] ) {
break;
}
}
if ( i == preambleLen ) {
ps.bytePos = preambleLen;
}
documentStartBytePos = ps.bytePos;
ps.eolNormalized = !normalize;
// decode first characters
ps.appendMode = true;
ReadData();
}
private void InitTextReaderInput( string baseUriStr, TextReader input ) {
InitTextReaderInput( baseUriStr, null, input );
}
private void InitTextReaderInput( string baseUriStr, Uri baseUri, TextReader input ) {
Debug.Assert( ps.charPos == 0 && ps.charsUsed == 0 && ps.stream == null );
Debug.Assert( baseUriStr != null );
ps.textReader = input;
ps.baseUriStr = baseUriStr;
ps.baseUri = baseUri;
if ( ps.chars == null ) {
ps.chars = new char[ XmlReader.DefaultBufferSize + 1 ];
}
ps.encoding = Encoding.Unicode;
ps.eolNormalized = !normalize;
// read first characters
ps.appendMode = true;
ReadData();
}
private void InitStringInput( string baseUriStr, Encoding originalEncoding, string str ) {
Debug.Assert( ps.stream == null && ps.textReader == null );
Debug.Assert( ps.charPos == 0 && ps.charsUsed == 0 );
Debug.Assert( baseUriStr != null );
ps.baseUriStr = baseUriStr;
ps.baseUri = null;
int len = str.Length;
ps.chars = new char[ len + 1 ];
str.CopyTo( 0, ps.chars, 0, str.Length );
ps.charsUsed = len;
ps.chars[len] = (char)0;
ps.encoding = originalEncoding;
ps.eolNormalized = !normalize;
ps.isEof = true;
}
#if !SILVERLIGHT
private void InitFragmentReader( XmlNodeType fragmentType, XmlParserContext parserContext, bool allowXmlDeclFragment ) {
fragmentParserContext = parserContext;
if ( parserContext != null ) {
if ( parserContext.NamespaceManager != null ) {
namespaceManager = parserContext.NamespaceManager;
xmlContext.defaultNamespace = namespaceManager.LookupNamespace( string.Empty );
}
else {
namespaceManager = new XmlNamespaceManager( nameTable );
}
ps.baseUriStr = parserContext.BaseURI;
ps.baseUri = null;
xmlContext.xmlLang = parserContext.XmlLang;
xmlContext.xmlSpace = parserContext.XmlSpace;
}
else {
namespaceManager = new XmlNamespaceManager( nameTable );
ps.baseUriStr = string.Empty;
ps.baseUri = null;
}
reportedBaseUri = ps.baseUriStr;
switch ( fragmentType ) {
case XmlNodeType.Attribute:
ps.appendMode = false;
parsingFunction = ParsingFunction.SwitchToInteractive;
nextParsingFunction = ParsingFunction.FragmentAttribute;
break;
case XmlNodeType.Element:
Debug.Assert( parsingFunction == ParsingFunction.SwitchToInteractiveXmlDecl );
nextParsingFunction = ParsingFunction.DocumentContent;
break;
case XmlNodeType.Document:
Debug.Assert( parsingFunction == ParsingFunction.SwitchToInteractiveXmlDecl );
Debug.Assert( nextParsingFunction == ParsingFunction.DocumentContent );
break;
case XmlNodeType.XmlDeclaration:
if ( allowXmlDeclFragment ) {
ps.appendMode = false;
parsingFunction = ParsingFunction.SwitchToInteractive;
nextParsingFunction = ParsingFunction.XmlDeclarationFragment;
break;
}
else {
goto default;
}
default:
Throw( Res.Xml_PartialContentNodeTypeNotSupportedEx, fragmentType.ToString() );
return;
}
this.fragmentType = fragmentType;
this.fragment = true;
}
#endif
private void ProcessDtdFromParserContext(XmlParserContext context) {
Debug.Assert( context != null && context.HasDtdInfo );
switch ( dtdProcessing ) {
case DtdProcessing.Prohibit:
ThrowWithoutLineInfo( Res.Xml_DtdIsProhibitedEx );
break;
case DtdProcessing.Ignore:
// do nothing
break;
case DtdProcessing.Parse:
ParseDtdFromParserContext();
break;
default:
Debug.Assert( false, "Unhandled DtdProcessing enumeration value." );
break;
}
}
#if !SILVERLIGHT // Needed only for XmlTextReader
// SxS: This method resolve Uri but does not expose it to the caller. It's OK to suppress the warning.
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[ResourceExposure(ResourceScope.None)]
private void OpenUrl() {
Debug.Assert( url != null && url.Length > 0 );
Debug.Assert( compressedStack != null );
XmlResolver tmpResolver;
if ( ps.baseUri != null ) {
Debug.Assert( xmlResolver != null );
tmpResolver = xmlResolver;
}
else {
tmpResolver = ( xmlResolver == null ) ? new XmlUrlResolver() : xmlResolver;
ps.baseUri = tmpResolver.ResolveUri( null, url );
ps.baseUriStr = ps.baseUri.ToString();
}
try {
CompressedStack.Run( compressedStack, new ContextCallback( OpenUrlDelegate ), tmpResolver );
}
catch {
SetErrorState();
throw;
}
if ( ps.stream == null ) {
ThrowWithoutLineInfo( Res.Xml_CannotResolveUrl, ps.baseUriStr );
}
InitStreamInput( ps.baseUri, ps.baseUriStr, ps.stream, null );
reportedEncoding = ps.encoding;
}
void OpenUrlDelegate(object xmlResolver) {
ps.stream = (Stream) ((XmlResolver)xmlResolver).GetEntity( ps.baseUri, null, typeof( Stream ) );
}
#endif
// Stream input only: detect encoding from the first 4 bytes of the byte buffer starting at ps.bytes[ps.bytePos]
private Encoding DetectEncoding() {
Debug.Assert( ps.bytes != null );
Debug.Assert( ps.bytePos == 0 );
if ( ps.bytesUsed < 2 ) {
return null;
}
int first2Bytes = ps.bytes[0] << 8 | ps.bytes[1];
int next2Bytes = ( ps.bytesUsed >= 4 ) ? ( ps.bytes[2] << 8 | ps.bytes[3] ) : 0;
switch ( first2Bytes ) {
#if !SILVERLIGHT // Removing USC4 encoding
case 0x0000:
switch ( next2Bytes ) {
case 0xFEFF:
return Ucs4Encoding.UCS4_Bigendian;
case 0x003C:
return Ucs4Encoding.UCS4_Bigendian;
case 0xFFFE:
return Ucs4Encoding.UCS4_2143;
case 0x3C00:
return Ucs4Encoding.UCS4_2143;
}
break;
#endif
case 0xFEFF:
#if SILVERLIGHT // Removing USC4 encoding
return Encoding.BigEndianUnicode;
#else
if (next2Bytes == 0x0000) {
return Ucs4Encoding.UCS4_3412;
}
else {
return Encoding.BigEndianUnicode;
}
#endif
case 0xFFFE:
#if SILVERLIGHT // Removing USC4 encoding
return Encoding.Unicode;
#else
if ( next2Bytes == 0x0000 ) {
return Ucs4Encoding.UCS4_Littleendian;
}
else {
return Encoding.Unicode;
}
#endif
case 0x3C00:
#if SILVERLIGHT // Removing USC4 encoding
return Encoding.Unicode;
#else
if ( next2Bytes == 0x0000 ) {
return Ucs4Encoding.UCS4_Littleendian;
}
else {
return Encoding.Unicode;
}
#endif
case 0x003C:
#if SILVERLIGHT // Removing USC4 encoding
return Encoding.BigEndianUnicode;
#else
if ( next2Bytes == 0x0000 ) {
return Ucs4Encoding.UCS4_3412;
}
else {
return Encoding.BigEndianUnicode;
}
#endif
case 0x4C6F:
if ( next2Bytes == 0xA794 ) {
Throw( Res.Xml_UnknownEncoding, "ebcdic" );
}
break;
case 0xEFBB:
if ( ( next2Bytes & 0xFF00 ) == 0xBF00 ) {
return new UTF8Encoding( true, true );
}
break;
}
// Default encoding is ASCII (using SafeAsciiDecoder) until we read xml declaration.
// If we set UTF8 encoding now, it will throw exceptions (=slow) when decoding non-UTF8-friendly
// characters after the xml declaration, which may be perfectly valid in the encoding
// specified in xml declaration.
return null;
}
private void SetupEncoding( Encoding encoding ) {
if ( encoding == null ) {
Debug.Assert( ps.charPos == 0 );
ps.encoding = Encoding.UTF8;
ps.decoder = new SafeAsciiDecoder();
}
else {
ps.encoding = encoding;
switch ( ps.encoding.WebName ) { // Encoding.Codepage is not supported in Silverlight
case "utf-16":
ps.decoder = new UTF16Decoder( false );
break;
case "utf-16BE":
ps.decoder = new UTF16Decoder( true );
break;
default:
ps.decoder = encoding.GetDecoder();
break;
}
}
}
// Switches the reader's encoding
private void SwitchEncoding( Encoding newEncoding ) {
#if SILVERLIGHT
if ( ( newEncoding.WebName != ps.encoding.WebName || ps.decoder is SafeAsciiDecoder ) ) {
#else
if ( ( newEncoding.WebName != ps.encoding.WebName || ps.decoder is SafeAsciiDecoder ) && !afterResetState) {
#endif
Debug.Assert( ps.stream != null );
UnDecodeChars();
ps.appendMode = false;
SetupEncoding( newEncoding );
ReadData();
}
}
// Returns the Encoding object for the given encoding name, if the reader's encoding can be switched to that encoding.
// Performs checks whether switching from current encoding to specified encoding is allowed.
private Encoding CheckEncoding( string newEncodingName ) {
// encoding can be switched on stream input only
if ( ps.stream == null ) {
return ps.encoding;
}
if ( 0 == String.Compare( newEncodingName, "ucs-2", StringComparison.OrdinalIgnoreCase ) ||
0 == String.Compare( newEncodingName, "utf-16", StringComparison.OrdinalIgnoreCase ) ||
0 == String.Compare( newEncodingName, "iso-10646-ucs-2", StringComparison.OrdinalIgnoreCase ) ||
0 == String.Compare( newEncodingName, "ucs-4", StringComparison.OrdinalIgnoreCase ) ) {
if ( ps.encoding.WebName != "utf-16BE" &&
ps.encoding.WebName != "utf-16" &&
0 != String.Compare( newEncodingName, "ucs-4", StringComparison.OrdinalIgnoreCase ) ) {
#if SILVERLIGHT // Needed only for XmlTextReader
ThrowWithoutLineInfo(Res.Xml_MissingByteOrderMark);
#else
if ( afterResetState ) {
Throw( Res.Xml_EncodingSwitchAfterResetState, newEncodingName );
}
else {
ThrowWithoutLineInfo( Res.Xml_MissingByteOrderMark );
}
#endif
}
return ps.encoding;
}
Encoding newEncoding = null;
if ( 0 == String.Compare( newEncodingName, "utf-8", StringComparison.OrdinalIgnoreCase ) ) {
newEncoding = new UTF8Encoding( true, true );
}
else {
try {
newEncoding = Encoding.GetEncoding( newEncodingName );
}
catch ( NotSupportedException innerEx ) {
Throw( Res.Xml_UnknownEncoding, newEncodingName, innerEx );
}
catch ( ArgumentException innerEx) {
Throw( Res.Xml_UnknownEncoding, newEncodingName, innerEx );
}
#if !SILVERLIGHT
Debug.Assert( newEncoding.EncodingName != "UTF-8" );
#endif
}
#if !SILVERLIGHT // Needed only for XmlTextReader
// check for invalid encoding switches after ResetState
if ( afterResetState && ps.encoding.WebName != newEncoding.WebName ) {
Throw( Res.Xml_EncodingSwitchAfterResetState, newEncodingName );
}
#endif
return newEncoding;
}
void UnDecodeChars() {
Debug.Assert( ps.stream != null && ps.decoder != null && ps.bytes != null );
Debug.Assert( ps.appendMode, "UnDecodeChars cannot be called after ps.appendMode has been changed to false" );
Debug.Assert(ps.charsUsed >= ps.charPos, "The current position must be in the valid character range.");
if (maxCharactersInDocument > 0) {
// We're returning back in the input (potentially) so we need to fixup
// the character counters to avoid counting some of them twice.
// The following code effectively rolls-back all decoded characters
// after the ps.charPos (which typically points to the first character
// after the XML decl).
Debug.Assert(charactersInDocument >= ps.charsUsed - ps.charPos,
"We didn't correctly count some of the decoded characters against the MaxCharactersInDocument.");
charactersInDocument -= ps.charsUsed - ps.charPos;
}
if (maxCharactersFromEntities > 0) {
if (InEntity) {
Debug.Assert(charactersFromEntities >= ps.charsUsed - ps.charPos,
"We didn't correctly count some of the decoded characters against the MaxCharactersFromEntities.");
charactersFromEntities -= ps.charsUsed - ps.charPos;
}
}
ps.bytePos = documentStartBytePos; // byte position after preamble
if ( ps.charPos > 0 ) {
ps.bytePos += ps.encoding.GetByteCount( ps.chars, 0, ps.charPos );
}
ps.charsUsed = ps.charPos;
ps.isEof = false;
}
private void SwitchEncodingToUTF8() {
SwitchEncoding( new UTF8Encoding( true, true ) );
}
// Reads more data to the character buffer, discarding already parsed chars / decoded bytes.
int ReadData() {
// Append Mode: Append new bytes and characters to the buffers, do not rewrite them. Allocate new buffers
// if the current ones are full
// Rewrite Mode: Reuse the buffers. If there is less than half of the char buffer left for new data, move
// the characters that has not been parsed yet to the front of the buffer. Same for bytes.
if ( ps.isEof ) {
return 0;
}
int charsRead;
if ( ps.appendMode ) {
// the character buffer is full -> allocate a new one
if ( ps.charsUsed == ps.chars.Length - 1 ) {
// invalidate node values kept in buffer - applies to attribute values only
for ( int i = 0; i < attrCount; i++ ) {
nodes[index + i + 1].OnBufferInvalidated();
}
char[] newChars = new char[ ps.chars.Length * 2 ];
BlockCopyChars( ps.chars, 0, newChars, 0, ps.chars.Length );
ps.chars = newChars;
}
if ( ps.stream != null ) {
// the byte buffer is full -> allocate a new one
if ( ps.bytesUsed - ps.bytePos < MaxByteSequenceLen ) {
if ( ps.bytes.Length - ps.bytesUsed < MaxByteSequenceLen ) {
byte[] newBytes = new byte[ ps.bytes.Length * 2 ];
BlockCopy( ps.bytes, 0, newBytes, 0, ps.bytesUsed );
ps.bytes = newBytes;
}
}
}
charsRead = ps.chars.Length - ps.charsUsed - 1;
if ( charsRead > ApproxXmlDeclLength ) {
charsRead = ApproxXmlDeclLength;
}
}
else {
int charsLen = ps.chars.Length;
if ( charsLen - ps.charsUsed <= charsLen/2 ) {
// invalidate node values kept in buffer - applies to attribute values only
for ( int i = 0; i < attrCount; i++ ) {
nodes[index + i + 1].OnBufferInvalidated();
}
// move unparsed characters to front, unless the whole buffer contains unparsed characters
int copyCharsCount = ps.charsUsed - ps.charPos;
if ( copyCharsCount < charsLen - 1 ) {
ps.lineStartPos = ps.lineStartPos - ps.charPos;
if ( copyCharsCount > 0 ) {
BlockCopyChars( ps.chars, ps.charPos, ps.chars, 0, copyCharsCount );
}
ps.charPos = 0;
ps.charsUsed = copyCharsCount;
}
else {
char[] newChars = new char[ ps.chars.Length * 2 ];
BlockCopyChars( ps.chars, 0, newChars, 0, ps.chars.Length );
ps.chars = newChars;
}
}
if ( ps.stream != null ) {
// move undecoded bytes to the front to make some space in the byte buffer
int bytesLeft = ps.bytesUsed - ps.bytePos;
if ( bytesLeft <= MaxBytesToMove ) {
if ( bytesLeft == 0 ) {
ps.bytesUsed = 0;
}
else {
BlockCopy( ps.bytes, ps.bytePos, ps.bytes, 0, bytesLeft );
ps.bytesUsed = bytesLeft;
}
ps.bytePos = 0;
}
}
charsRead = ps.chars.Length - ps.charsUsed - 1;
}
if ( ps.stream != null ) {
if ( !ps.isStreamEof ) {
// read new bytes
if ( ps.bytePos == ps.bytesUsed && ps.bytes.Length - ps.bytesUsed > 0 ) {
int read = ps.stream.Read( ps.bytes, ps.bytesUsed, ps.bytes.Length - ps.bytesUsed );
if ( read == 0 ) {
ps.isStreamEof = true;
}
ps.bytesUsed += read;
}
}
int originalBytePos = ps.bytePos;
// decode chars
charsRead = GetChars( charsRead );
if ( charsRead == 0 && ps.bytePos != originalBytePos ) {
// GetChars consumed some bytes but it was not enough bytes to form a character -> try again
return ReadData();
}
}
else if ( ps.textReader != null ) {
// read chars
charsRead = ps.textReader.Read( ps.chars, ps.charsUsed, ps.chars.Length - ps.charsUsed - 1 );
ps.charsUsed += charsRead;
}
else {
charsRead = 0;
}
RegisterConsumedCharacters(charsRead, InEntity);
if ( charsRead == 0 ) {
Debug.Assert ( ps.charsUsed < ps.chars.Length );
ps.isEof = true;
}
ps.chars[ ps.charsUsed ] = (char)0;
return charsRead;
}
// Stream input only: read bytes from stream and decodes them according to the current encoding
int GetChars( int maxCharsCount ) {
Debug.Assert( ps.stream != null && ps.decoder != null && ps.bytes != null );
Debug.Assert( maxCharsCount <= ps.chars.Length - ps.charsUsed - 1 );
// determine the maximum number of bytes we can pass to the decoder
int bytesCount = ps.bytesUsed - ps.bytePos;
if ( bytesCount == 0 ) {
return 0;
}
int charsCount;
bool completed;
try {
// decode chars
ps.decoder.Convert( ps.bytes, ps.bytePos, bytesCount, ps.chars, ps.charsUsed, maxCharsCount, false, out bytesCount, out charsCount, out completed );
}
catch ( ArgumentException ) {
InvalidCharRecovery( ref bytesCount, out charsCount );
}
// move pointers and return
ps.bytePos += bytesCount;
ps.charsUsed += charsCount;
Debug.Assert( maxCharsCount >= charsCount );
return charsCount;
}
private void InvalidCharRecovery( ref int bytesCount, out int charsCount ) {
int charsDecoded = 0;
int bytesDecoded = 0;
try {
while ( bytesDecoded < bytesCount ) {
int chDec;
int bDec;
bool completed;
ps.decoder.Convert( ps.bytes, ps.bytePos + bytesDecoded, 1, ps.chars, ps.charsUsed + charsDecoded, 1, false, out bDec, out chDec, out completed );
charsDecoded += chDec;
bytesDecoded += bDec;
}
Debug.Assert( false, "We should get an exception again." );
}
catch ( ArgumentException ) {
}
if ( charsDecoded == 0 ) {
Throw( ps.charsUsed, Res.Xml_InvalidCharInThisEncoding );
}
charsCount = charsDecoded;
bytesCount = bytesDecoded;
}
internal void Close( bool closeInput ) {
if ( parsingFunction == ParsingFunction.ReaderClosed ) {
return;
}
while ( InEntity ) {
PopParsingState();
}
ps.Close( closeInput );
curNode = NodeData.None;
parsingFunction = ParsingFunction.ReaderClosed;
reportedEncoding = null;
reportedBaseUri = string.Empty;
readState = ReadState.Closed;
fullAttrCleanup = false;
ResetAttributes();
}
void ShiftBuffer( int sourcePos, int destPos, int count ) {
BlockCopyChars( ps.chars, sourcePos, ps.chars, destPos, count );
}
// Parses the xml or text declaration and switched encoding if needed
private bool ParseXmlDeclaration( bool isTextDecl ) {
while ( ps.charsUsed - ps.charPos < 6 ) { // minimum "
Encoding encoding = null;
for (;;) {
int originalSbLen = sb.Length;
int wsCount = EatWhitespaces( xmlDeclState == 0 ? null : sb );
// end of xml declaration
if ( ps.chars[ps.charPos] == '?' ) {
sb.Length = originalSbLen;
if ( ps.chars[ps.charPos + 1] == '>' ) {
if ( xmlDeclState == 0 ) {
Throw( isTextDecl ? Res.Xml_InvalidTextDecl : Res.Xml_InvalidXmlDecl );
}
ps.charPos += 2;
if ( !isTextDecl ) {
curNode.SetValue( sb.ToString() );
sb.Length = 0;
nextParsingFunction = parsingFunction;
parsingFunction = ParsingFunction.ResetAttributesRootLevel;
}
// switch to encoding specified in xml declaration
if ( encoding == null ) {
if ( isTextDecl ) {
Throw( Res.Xml_InvalidTextDecl );
}
#if !SILVERLIGHT // Needed only for XmlTextReader
if ( afterResetState ) {
// check for invalid encoding switches to default encoding
string encodingName = ps.encoding.WebName;
if ( encodingName != "utf-8" && encodingName != "utf-16" &&
encodingName != "utf-16BE" && !( ps.encoding is Ucs4Encoding ) ) {
Throw( Res.Xml_EncodingSwitchAfterResetState, ( ps.encoding.GetByteCount( "A" ) == 1 ) ? "UTF-8" : "UTF-16" );
}
}
#endif
if ( ps.decoder is SafeAsciiDecoder ) {
SwitchEncodingToUTF8();
}
}
else {
SwitchEncoding( encoding );
}
ps.appendMode = false;
return true;
}
else if ( ps.charPos + 1 == ps.charsUsed ) {
goto ReadData;
}
else {
ThrowUnexpectedToken( "'>'" );
}
}
if ( wsCount == 0 && xmlDeclState != 0 ) {
ThrowUnexpectedToken( "?>" );
}
// read attribute name
int nameEndPos = ParseName();
NodeData attr = null;
switch ( ps.chars[ps.charPos] ) {
case 'v':
if ( XmlConvert.StrEqual( ps.chars, ps.charPos, nameEndPos - ps.charPos, "version" ) && xmlDeclState == 0 ) {
if ( !isTextDecl ) {
attr = AddAttributeNoChecks( "version", 1 );
}
break;
}
goto default;
case 'e':
if ( XmlConvert.StrEqual( ps.chars, ps.charPos, nameEndPos - ps.charPos, "encoding" ) &&
( xmlDeclState == 1 || ( isTextDecl && xmlDeclState == 0 ) ) ) {
if ( !isTextDecl ) {
attr = AddAttributeNoChecks( "encoding", 1 );
}
xmlDeclState = 1;
break;
}
goto default;
case 's':
if ( XmlConvert.StrEqual( ps.chars, ps.charPos, nameEndPos - ps.charPos, "standalone" ) &&
( xmlDeclState == 1 || xmlDeclState == 2 ) && !isTextDecl ) {
if ( !isTextDecl ) {
attr = AddAttributeNoChecks( "standalone", 1 );
}
xmlDeclState = 2;
break;
}
goto default;
default:
Throw( isTextDecl ? Res.Xml_InvalidTextDecl : Res.Xml_InvalidXmlDecl );
break;
}
if ( !isTextDecl ) {
attr.SetLineInfo( ps.LineNo, ps.LinePos );
}
sb.Append( ps.chars, ps.charPos, nameEndPos - ps.charPos );
ps.charPos = nameEndPos;
// parse equals and quote char;
if ( ps.chars[ps.charPos] != '=' ) {
EatWhitespaces( sb );
if ( ps.chars[ps.charPos] != '=' ) {
ThrowUnexpectedToken( "=" );
}
}
sb.Append( '=' );
ps.charPos++;
char quoteChar = ps.chars[ps.charPos];
if ( quoteChar != '"' && quoteChar != '\'' ) {
EatWhitespaces( sb );
quoteChar = ps.chars[ps.charPos];
if ( quoteChar != '"' && quoteChar != '\'' ) {
ThrowUnexpectedToken( "\"", "'" );
}
}
sb.Append( quoteChar );
ps.charPos++;
if ( !isTextDecl ) {
attr.quoteChar = quoteChar;
attr.SetLineInfo2( ps.LineNo, ps.LinePos );
}
// parse attribute value
int pos = ps.charPos;
char[] chars;
Continue:
chars = ps.chars;
#if SILVERLIGHT
while (xmlCharType.IsAttributeValueChar(chars[pos])) {
pos++;
}
#else // Optimization due to the lack of inlining when a method uses byte*
unsafe {
while ( ( (xmlCharType.charProperties[chars[pos]] & XmlCharType.fAttrValue) != 0) ) {
pos++;
}
}
#endif
if ( ps.chars[pos] == quoteChar ) {
switch ( xmlDeclState ) {
// version
case 0:
#if XML10_FIFTH_EDITION
// VersionNum ::= '1.' [0-9]+ (starting with XML Fifth Edition)
if ( pos - ps.charPos >= 3 &&
ps.chars[ps.charPos] == '1' &&
ps.chars[ps.charPos + 1] == '.' &&
XmlCharType.IsOnlyDigits( ps.chars, ps.charPos + 2, pos - ps.charPos - 2 ) ) {
#else
// VersionNum ::= '1.0' (XML Fourth Edition and earlier)
if ( XmlConvert.StrEqual( ps.chars, ps.charPos, pos - ps.charPos, "1.0" ) ) {
#endif
if ( !isTextDecl ) {
attr.SetValue( ps.chars, ps.charPos, pos - ps.charPos );
}
xmlDeclState = 1;
}
else {
string badVersion = new string( ps.chars, ps.charPos, pos - ps.charPos );
Throw( Res.Xml_InvalidVersionNumber, badVersion );
}
break;
case 1:
string encName = new string( ps.chars, ps.charPos, pos - ps.charPos );
encoding = CheckEncoding( encName );
if ( !isTextDecl ) {
attr.SetValue( encName );
}
xmlDeclState = 2;
break;
case 2:
if ( XmlConvert.StrEqual( ps.chars, ps.charPos, pos - ps.charPos, "yes" ) ) {
this.standalone = true;
}
else if ( XmlConvert.StrEqual( ps.chars, ps.charPos, pos - ps.charPos, "no" ) ) {
this.standalone = false;
}
else {
Debug.Assert( !isTextDecl );
Throw( Res.Xml_InvalidXmlDecl, ps.LineNo, ps.LinePos - 1 );
}
if ( !isTextDecl ) {
attr.SetValue( ps.chars, ps.charPos, pos - ps.charPos );
}
xmlDeclState = 3;
break;
default:
Debug.Assert( false );
break;
}
sb.Append( chars, ps.charPos, pos - ps.charPos );
sb.Append( quoteChar );
ps.charPos = pos + 1;
continue;
}
else if ( pos == ps.charsUsed ) {
if ( ReadData() != 0 ) {
goto Continue;
}
else {
Throw( Res.Xml_UnclosedQuote );
}
}
else {
Throw( isTextDecl ? Res.Xml_InvalidTextDecl : Res.Xml_InvalidXmlDecl );
}
ReadData:
if ( ps.isEof || ReadData() == 0 ) {
Throw( Res.Xml_UnexpectedEOF1 );
}
}
NoXmlDecl:
// no xml declaration
if ( !isTextDecl ) {
parsingFunction = nextParsingFunction;
}
#if !SILVERLIGHT // Needed only for XmlTextReader
if ( afterResetState ) {
// check for invalid encoding switches to default encoding
string encodingName = ps.encoding.WebName;
if ( encodingName != "utf-8" && encodingName != "utf-16" &&
encodingName != "utf-16BE" && !( ps.encoding is Ucs4Encoding ) ) {
Throw( Res.Xml_EncodingSwitchAfterResetState, ( ps.encoding.GetByteCount( "A" ) == 1 ) ? "UTF-8" : "UTF-16" );
}
}
#endif
if ( ps.decoder is SafeAsciiDecoder ) {
SwitchEncodingToUTF8();
}
ps.appendMode = false;
return false;
}
// Parses the document content
private bool ParseDocumentContent() {
for (;;) {
bool needMoreChars = false;
int pos = ps.charPos;
char[] chars = ps.chars;
// some tag
if ( chars[pos] == '<' ) {
needMoreChars = true;
if ( ps.charsUsed - pos < 4 ) // minimum ""
goto ReadData;
pos++;
switch ( chars[pos] ) {
// processing instruction
case '?':
ps.charPos = pos + 1;
if ( ParsePI() ) {
return true;
}
continue;
case '!':
pos++;
if ( ps.charsUsed - pos < 2 ) // minimum characters expected "--"
goto ReadData;
// comment
if ( chars[pos] == '-' ) {
if ( chars[pos+1] == '-' ) {
ps.charPos = pos + 2;
if ( ParseComment() ) {
return true;
}
continue;
}
else {
ThrowUnexpectedToken( pos + 1, "-" );
}
}
// CDATA section
else if ( chars[pos] == '[' ) {
if ( fragmentType != XmlNodeType.Document ) {
pos++;
if ( ps.charsUsed - pos < 6 ) {
goto ReadData;
}
if ( XmlConvert.StrEqual( chars, pos, 6, "CDATA[" ) ) {
ps.charPos = pos + 6;
ParseCData();
if ( fragmentType == XmlNodeType.None ) {
fragmentType = XmlNodeType.Element;
}
return true;
}
else {
ThrowUnexpectedToken( pos, "CDATA[" );
}
}
else {
Throw( ps.charPos, Res.Xml_InvalidRootData );
}
}
// DOCTYPE declaration
else {
if ( fragmentType == XmlNodeType.Document || fragmentType == XmlNodeType.None ) {
fragmentType = XmlNodeType.Document;
ps.charPos = pos;
if ( ParseDoctypeDecl() ) {
return true;
}
continue;
}
else {
if ( ParseUnexpectedToken( pos ) == "DOCTYPE" ) {
Throw( Res.Xml_BadDTDLocation );
}
else {
ThrowUnexpectedToken( pos, "" ) );
ps.charPos -= 3;
incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.CDATA:
if ( ParseCDataOrComment( XmlNodeType.CDATA, out startPos, out pos ) ) {
Debug.Assert( XmlConvert.StrEqual( ps.chars, ps.charPos - 3, 3, "]]>" ) );
ps.charPos -= 3;
incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.EndElement:
parsingFunction = ParsingFunction.PopElementContext;
nextParsingFunction = ( index > 0 || fragmentType != XmlNodeType.Document ) ? ParsingFunction.ElementContent
: ParsingFunction.DocumentContent;
outerReader.Read();
incReadState = IncrementalReadState.End;
goto case IncrementalReadState.End;
case IncrementalReadState.End:
return charsDecoded;
case IncrementalReadState.ReadData:
if ( ReadData() == 0 ) {
ThrowUnclosedElements();
}
incReadState = IncrementalReadState.Text;
startPos = ps.charPos;
pos = startPos;
break;
default:
Debug.Assert( false );
break;
}
Debug.Assert( incReadState == IncrementalReadState.Text ||
incReadState == IncrementalReadState.Attributes ||
incReadState == IncrementalReadState.AttributeValue );
char[] chars = ps.chars;
startPos = ps.charPos;
pos = startPos;
for (;;) {
incReadLineInfo.Set( ps.LineNo, ps.LinePos );
char c;
unsafe {
if ( incReadState == IncrementalReadState.Attributes ) {
while ( ( ( xmlCharType.charProperties[c = chars[pos]] & XmlCharType.fAttrValue ) != 0 ) && c != '/' ) {
pos++;
}
}
else {
while ( ( ( xmlCharType.charProperties[c = chars[pos]] & XmlCharType.fAttrValue ) != 0 ) ) {
pos++;
}
}
}
if ( chars[pos] == '&' || chars[pos] == (char)0x9 ) {
pos++;
continue;
}
if ( pos - startPos > 0 ) {
goto AppendAndUpdateCharPos;
}
switch ( chars[pos] ) {
// eol
case (char)0xA:
pos++;
OnNewLine( pos );
continue;
case (char)0xD:
if ( chars[pos+1] == (char)0xA ) {
pos += 2;
}
else if ( pos+1 < ps.charsUsed ) {
pos++;
}
else {
goto ReadData;
}
OnNewLine( pos );
continue;
// some tag
case '<':
if ( incReadState != IncrementalReadState.Text ) {
pos++;
continue;
}
if ( ps.charsUsed - pos < 2 ) {
goto ReadData;
}
switch ( chars[pos+1] ) {
// pi
case '?':
pos += 2;
incReadState = IncrementalReadState.PI;
goto AppendAndUpdateCharPos;
// comment
case '!':
if ( ps.charsUsed - pos < 4 ) {
goto ReadData;
}
if ( chars[pos+2] == '-' && chars[pos+3] == '-' ) {
pos += 4;
incReadState = IncrementalReadState.Comment;
goto AppendAndUpdateCharPos;
}
if ( ps.charsUsed - pos < 9 ) {
goto ReadData;
}
if ( XmlConvert.StrEqual( chars, pos + 2, 7, "[CDATA[" ) ) {
pos += 9;
incReadState = IncrementalReadState.CDATA;
goto AppendAndUpdateCharPos;
}
else {
;//Throw( );
}
break;
// end tag
case '/':
{
Debug.Assert( ps.charPos - pos == 0 );
Debug.Assert( ps.charPos - startPos == 0);
int colonPos;
// ParseQName can flush the buffer, so we need to update the startPos, pos and chars after calling it
int endPos = ParseQName( true, 2, out colonPos );
if ( XmlConvert.StrEqual( chars, ps.charPos + 2, endPos - ps.charPos - 2, curNode.GetNameWPrefix( nameTable ) ) &&
( ps.chars[endPos] == '>' || xmlCharType.IsWhiteSpace( ps.chars[endPos] ) ) ) {
if ( --incReadDepth > 0 ) {
pos = endPos + 1;
continue;
}
ps.charPos = endPos;
if ( xmlCharType.IsWhiteSpace( ps.chars[endPos] ) ) {
EatWhitespaces( null );
}
if ( ps.chars[ps.charPos] != '>' ) {
ThrowUnexpectedToken( ">" );
}
ps.charPos++;
incReadState = IncrementalReadState.EndElement;
goto OuterContinue;
}
else {
pos = endPos;
startPos = ps.charPos;
chars = ps.chars;
continue;
}
}
// start tag
default:
{
Debug.Assert( ps.charPos - pos == 0 );
Debug.Assert( ps.charPos - startPos == 0 );
int colonPos;
// ParseQName can flush the buffer, so we need to update the startPos, pos and chars after calling it
int endPos = ParseQName( true, 1, out colonPos );
if ( XmlConvert.StrEqual( ps.chars, ps.charPos + 1, endPos - ps.charPos - 1, curNode.localName ) &&
( ps.chars[endPos] == '>' || ps.chars[endPos] == '/' || xmlCharType.IsWhiteSpace( ps.chars[endPos] ) ) ) {
incReadDepth++;
incReadState = IncrementalReadState.Attributes;
pos = endPos;
goto AppendAndUpdateCharPos;
}
pos = endPos;
startPos = ps.charPos;
chars = ps.chars;
continue;
}
}
break;
// end of start tag
case '/':
if ( incReadState == IncrementalReadState.Attributes ) {
if ( ps.charsUsed - pos < 2 ) {
goto ReadData;
}
if ( chars[pos+1] == '>' ) {
incReadState = IncrementalReadState.Text;
incReadDepth--;
}
}
pos++;
continue;
// end of start tag
case '>':
if ( incReadState == IncrementalReadState.Attributes ) {
incReadState = IncrementalReadState.Text;
}
pos++;
continue;
case '"':
case '\'':
switch ( incReadState ) {
case IncrementalReadState.AttributeValue:
if ( chars[pos] == curNode.quoteChar ) {
incReadState = IncrementalReadState.Attributes;
}
break;
case IncrementalReadState.Attributes:
curNode.quoteChar = chars[pos];
incReadState = IncrementalReadState.AttributeValue;
break;
}
pos++;
continue;
default:
// end of buffer
if ( pos == ps.charsUsed ) {
goto ReadData;
}
// surrogate chars or invalid chars are ignored
else {
pos++;
continue;
}
}
}
ReadData:
incReadState = IncrementalReadState.ReadData;
AppendAndUpdateCharPos:
ps.charPos = pos;
Append:
// decode characters
int charsParsed = pos - startPos;
if ( charsParsed > 0 ) {
int count;
try {
count = incReadDecoder.Decode( ps.chars, startPos, charsParsed );
}
catch ( XmlException e ) {
ReThrow( e, (int)incReadLineInfo.lineNo, (int)incReadLineInfo.linePos );
return 0;
}
Debug.Assert( count == charsParsed || incReadDecoder.IsFull, "Check if decoded consumed all characters unless it's full." );
charsDecoded += count;
if ( incReadDecoder.IsFull ) {
incReadLeftStartPos = startPos + count;
incReadLeftEndPos = pos;
incReadLineInfo.linePos += count; // we have never more than 1 line cached
return charsDecoded;
}
}
}
}
private void FinishIncrementalRead() {
incReadDecoder = new IncrementalReadDummyDecoder();
IncrementalRead();
Debug.Assert( IncrementalRead() == 0, "Previous call of IncrementalRead should eat up all characters!" );
incReadDecoder = null;
}
private bool ParseFragmentAttribute() {
Debug.Assert( fragmentType == XmlNodeType.Attribute );
// if first call then parse the whole attribute value
if ( curNode.type == XmlNodeType.None ) {
curNode.type = XmlNodeType.Attribute;
curAttrIndex = 0;
ParseAttributeValueSlow( ps.charPos, ' ', curNode ); // The quote char is intentionally empty (space) because we need to parse ' and " into the attribute value
}
else {
parsingFunction = ParsingFunction.InReadAttributeValue;
}
// return attribute value chunk
if ( ReadAttributeValue() ) {
Debug.Assert( parsingFunction == ParsingFunction.InReadAttributeValue );
parsingFunction = ParsingFunction.FragmentAttribute;
return true;
}
else {
OnEof();
return false;
}
}
#endif
#if !SILVERLIGHT // Needed only for XmlTextReader (ReadChars, ReadBase64, ReadBinHex)
private bool ParseAttributeValueChunk() {
char[] chars = ps.chars;
int pos = ps.charPos;
curNode = AddNode( index + attrCount + 1, index + 2 );
curNode.SetLineInfo( ps.LineNo, ps.LinePos );
if ( emptyEntityInAttributeResolved ) {
curNode.SetValueNode( XmlNodeType.Text, string.Empty );
emptyEntityInAttributeResolved = false;
return true;
}
Debug.Assert( stringBuilder.Length == 0 );
for (;;) {
unsafe {
while ( ( ( xmlCharType.charProperties[chars[pos]] & XmlCharType.fAttrValue ) != 0 ) )
pos++;
}
switch ( chars[pos] ) {
// eol D
case (char)0xD:
Debug.Assert( ps.eolNormalized, "Entity replacement text for attribute values should be EOL-normalized!" );
pos++;
continue;
// eol A, tab
case (char)0xA:
case (char)0x9:
if ( normalize ) {
chars[pos] = (char)0x20; // CDATA normalization of 0xA and 0x9
}
pos++;
continue;
case '"':
case '\'':
case '>':
pos++;
continue;
// attribute values cannot contain '<'
case '<':
Throw( pos, Res.Xml_BadAttributeChar, XmlException.BuildCharExceptionArgs( '<', '\0' ) );
break;
// entity reference
case '&':
if ( pos - ps.charPos > 0 ) {
stringBuilder.Append( chars, ps.charPos, pos - ps.charPos );
}
ps.charPos = pos;
// expand char entities but not general entities
switch ( HandleEntityReference( true, EntityExpandType.OnlyCharacter, out pos ) ) {
case EntityType.CharacterDec:
case EntityType.CharacterHex:
case EntityType.CharacterNamed:
chars = ps.chars;
if ( normalize && xmlCharType.IsWhiteSpace( chars[ps.charPos] ) && pos - ps.charPos == 1 ) {
chars[ps.charPos] = (char)0x20; // CDATA normalization of character references in entities
}
break;
case EntityType.Unexpanded:
if ( stringBuilder.Length == 0 ) {
curNode.lineInfo.linePos++;
ps.charPos++;
curNode.SetNamedNode( XmlNodeType.EntityReference, ParseEntityName() );
return true;
}
else {
goto ReturnText;
}
default:
Debug.Assert( false, "We should never get to this point." );
break;
}
chars = ps.chars;
continue;
default:
// end of buffer
if ( pos == ps.charsUsed ) {
goto ReadData;
}
// surrogate chars
else {
char ch = chars[pos];
if ( XmlCharType.IsHighSurrogate(ch) ) {
if ( pos + 1 == ps.charsUsed ) {
goto ReadData;
}
pos++;
if ( XmlCharType.IsLowSurrogate( chars[pos] ) ) {
pos++;
continue;
}
}
ThrowInvalidChar( chars, ps.charsUsed, pos );
break;
}
}
ReadData:
if ( pos - ps.charPos > 0 ) {
stringBuilder.Append( chars, ps.charPos, pos - ps.charPos );
ps.charPos = pos;
}
// read new characters into the buffer
if ( ReadData() == 0 ) {
if ( stringBuilder.Length > 0 ) {
goto ReturnText;
}
else {
if ( HandleEntityEnd( false ) ) {
SetupEndEntityNodeInAttribute();
return true;
}
else {
Debug.Assert( false, "We should never get to this point." );
}
}
}
pos = ps.charPos;
chars = ps.chars;
}
ReturnText:
if ( pos - ps.charPos > 0 ) {
stringBuilder.Append( chars, ps.charPos, pos - ps.charPos );
ps.charPos = pos;
}
curNode.SetValueNode( XmlNodeType.Text, stringBuilder.ToString() );
stringBuilder.Length = 0;
return true;
}
private void ParseXmlDeclarationFragment() {
try {
ParseXmlDeclaration( false );
}
catch ( XmlException e ) {
ReThrow( e, e.LineNumber, e.LinePosition - 6 ); // 6 == strlen( " create a new one
else if ( nt == null ) {
nt = new NameTable();
Debug.Assert( nameTableFromSettings == false );
}
nameTable = nt;
// make sure we have namespace manager
if ( namespaceManager == null ) {
namespaceManager = new XmlNamespaceManager( nt );
}
// copy xml:space and xml:lang
xmlContext.xmlSpace = context.XmlSpace;
xmlContext.xmlLang = context.XmlLang;
}
//
// DtdInfo
//
#if !SILVERLIGHT
internal override IDtdInfo DtdInfo {
get {
return dtdInfo;
}
}
internal void SetDtdInfo(IDtdInfo newDtdInfo) {
Debug.Assert( dtdInfo == null );
dtdInfo = newDtdInfo;
if ( dtdInfo != null ) {
if ( ( validatingReaderCompatFlag || !v1Compat ) && ( dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes ) ) {
addDefaultAttributesAndNormalize = true;
}
}
}
#endif
//
// Validation support
//
#if !SILVERLIGHT // no validation in Silverlight
internal IValidationEventHandling ValidationEventHandling {
set {
validationEventHandling = value;
}
}
internal OnDefaultAttributeUseDelegate OnDefaultAttributeUse {
set { onDefaultAttributeUse = value; }
}
#endif
//
// Internal properties for XmlValidatingReader
//
#if !SILVERLIGHT // Needed only for XmlValidatingReader
internal bool XmlValidatingReaderCompatibilityMode {
set {
validatingReaderCompatFlag = value;
// Fix for VSWhidbey 516556; These namespaces must be added to the nametable for back compat reasons.
if ( value ) {
nameTable.Add( XmlReservedNs.NsXs ); // Note: this is equal to XmlReservedNs.NsXsd in Everett
nameTable.Add( XmlReservedNs.NsXsi );
nameTable.Add( XmlReservedNs.NsDataType );
}
}
}
internal XmlNodeType FragmentType {
get {
return fragmentType;
}
}
internal void ChangeCurrentNodeType( XmlNodeType newNodeType ) {
Debug.Assert( curNode.type == XmlNodeType.Whitespace && newNodeType == XmlNodeType.SignificantWhitespace, "Incorrect node type change!" );
curNode.type = newNodeType;
}
internal XmlResolver GetResolver() {
return xmlResolver;
}
internal object InternalSchemaType {
get {
return curNode.schemaType;
}
set {
curNode.schemaType = value;
}
}
internal object InternalTypedValue {
get {
return curNode.typedValue;
}
set {
curNode.typedValue = value;
}
}
internal bool StandAlone {
get {
return standalone;
}
}
internal override XmlNamespaceManager NamespaceManager {
get {
return namespaceManager;
}
}
internal bool V1Compat {
get {
return v1Compat;
}
}
internal ConformanceLevel V1ComformanceLevel {
get {
return fragmentType == XmlNodeType.Element ? ConformanceLevel.Fragment : ConformanceLevel.Document;
}
}
#endif
private bool AddDefaultAttributeDtd(IDtdDefaultAttributeInfo defAttrInfo, bool definedInDtd, NodeData[] nameSortedNodeData) {
if ( defAttrInfo.Prefix.Length > 0 ) {
attrNeedNamespaceLookup = true;
}
string localName = defAttrInfo.LocalName;
string prefix = defAttrInfo.Prefix;
// check for duplicates
if (nameSortedNodeData != null) {
if (Array.BinarySearch