|
//------------------------------------------------------------------------------
// <copyright file="DtdParser.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.Text;
using System.Xml.Schema;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
#if SILVERLIGHT
using BufferBuilder=System.Xml.BufferBuilder;
#else
using BufferBuilder = System.Text.StringBuilder;
#endif
namespace System.Xml {
internal partial class DtdParser : IDtdParser {
//
// Private types
//
enum Token {
CDATA = XmlTokenizedType.CDATA, // == 0
ID = XmlTokenizedType.ID, // == 1
IDREF = XmlTokenizedType.IDREF, // == 2
IDREFS = XmlTokenizedType.IDREFS, // == 3
ENTITY = XmlTokenizedType.ENTITY, // == 4
ENTITIES = XmlTokenizedType.ENTITIES, // == 5
NMTOKEN = XmlTokenizedType.NMTOKEN, // == 6
NMTOKENS = XmlTokenizedType.NMTOKENS, // == 7
NOTATION = XmlTokenizedType.NOTATION, // == 8
None ,
PERef ,
AttlistDecl ,
ElementDecl ,
EntityDecl ,
NotationDecl ,
Comment ,
PI ,
CondSectionStart,
CondSectionEnd ,
Eof ,
REQUIRED ,
IMPLIED ,
FIXED ,
QName ,
Name ,
Nmtoken ,
Quote,
LeftParen ,
RightParen ,
GreaterThan ,
Or ,
LeftBracket ,
RightBracket ,
PUBLIC ,
SYSTEM ,
Literal ,
DOCTYPE ,
NData ,
Percent ,
Star ,
QMark ,
Plus ,
PCDATA ,
Comma ,
ANY ,
EMPTY ,
IGNORE ,
INCLUDE ,
}
enum ScanningFunction {
SubsetContent,
Name,
QName,
Nmtoken,
Doctype1,
Doctype2,
Element1,
Element2,
Element3,
Element4,
Element5,
Element6,
Element7,
Attlist1,
Attlist2,
Attlist3,
Attlist4,
Attlist5,
Attlist6,
Attlist7,
Entity1,
Entity2,
Entity3,
Notation1,
CondSection1,
CondSection2,
CondSection3,
Literal,
SystemId,
PublicId1,
PublicId2,
ClosingTag,
ParamEntitySpace,
None,
}
enum LiteralType {
AttributeValue,
EntityReplText,
SystemOrPublicID
}
#if !SILVERLIGHT
class UndeclaredNotation {
internal string name;
internal int lineNo;
internal int linePos;
internal UndeclaredNotation next;
internal UndeclaredNotation( string name, int lineNo, int linePos ) {
this.name = name;
this.lineNo = lineNo;
this.linePos = linePos;
this.next = null;
}
}
#endif
//
// Fields
//
// connector to reader
IDtdParserAdapter readerAdapter;
#if !SILVERLIGHT
IDtdParserAdapterWithValidation readerAdapterWithValidation;
#endif
// name table
XmlNameTable nameTable;
// final schema info
SchemaInfo schemaInfo;
// XmlCharType instance
XmlCharType xmlCharType = XmlCharType.Instance;
// system & public id
string systemId = string.Empty;
string publicId = string.Empty;
// flags
#if !SILVERLIGHT
bool normalize = true;
bool validate = false;
bool supportNamespaces = true;
bool v1Compat = false;
#endif
// cached character buffer
char[] chars;
int charsUsed;
int curPos;
// scanning function for the next token
ScanningFunction scanningFunction;
ScanningFunction nextScaningFunction;
ScanningFunction savedScanningFunction; // this one is used only for adding spaces around parameter entities
// flag if whitespace seen before token
bool whitespaceSeen;
// start position of the last token (for name and value)
int tokenStartPos;
// colon position (for name)
int colonPos;
// value of the internal subset
BufferBuilder internalSubsetValueSb = null;
// entities
int externalEntitiesDepth = 0;
int currentEntityId = 0;
// free-floating DTD support
bool freeFloatingDtd = false;
bool hasFreeFloatingInternalSubset = false;
// misc
BufferBuilder stringBuilder;
int condSectionDepth = 0;
LineInfo literalLineInfo = new LineInfo( 0, 0 );
char literalQuoteChar = '"';
string documentBaseUri = string.Empty;
string externalDtdBaseUri = string.Empty;
#if !SILVERLIGHT
Dictionary<string, UndeclaredNotation> undeclaredNotations = null;
int[] condSectionEntityIds = null;
#endif
const int CondSectionEntityIdsInitialSize = 2;
//
// Constructor
//
static DtdParser() {
#if DEBUG
// The absolute numbering is utilized in attribute type parsing
Debug.Assert( (int)Token.CDATA == (int)XmlTokenizedType.CDATA && (int)XmlTokenizedType.CDATA == 0 );
Debug.Assert( (int)Token.ID == (int)XmlTokenizedType.ID && (int)XmlTokenizedType.ID == 1 );
Debug.Assert( (int)Token.IDREF == (int)XmlTokenizedType.IDREF && (int)XmlTokenizedType.IDREF == 2 );
Debug.Assert( (int)Token.IDREFS == (int)XmlTokenizedType.IDREFS && (int)XmlTokenizedType.IDREFS == 3 );
Debug.Assert( (int)Token.ENTITY == (int)XmlTokenizedType.ENTITY && (int)XmlTokenizedType.ENTITY == 4 );
Debug.Assert( (int)Token.ENTITIES == (int)XmlTokenizedType.ENTITIES && (int)XmlTokenizedType.ENTITIES == 5 );
Debug.Assert( (int)Token.NMTOKEN == (int)XmlTokenizedType.NMTOKEN && (int)XmlTokenizedType.NMTOKEN == 6 );
Debug.Assert( (int)Token.NMTOKENS == (int)XmlTokenizedType.NMTOKENS && (int)XmlTokenizedType.NMTOKENS == 7 );
Debug.Assert( (int)Token.NOTATION == (int)XmlTokenizedType.NOTATION && (int)XmlTokenizedType.NOTATION == 8 );
#endif
}
DtdParser() {
}
internal static IDtdParser Create() {
return new DtdParser();
}
//
// Initialization methods
//
private void Initialize(IDtdParserAdapter readerAdapter) {
Debug.Assert(readerAdapter != null);
this.readerAdapter = readerAdapter;
#if !SILVERLIGHT
this.readerAdapterWithValidation = readerAdapter as IDtdParserAdapterWithValidation;
#endif
nameTable = readerAdapter.NameTable;
#if !SILVERLIGHT
IDtdParserAdapterWithValidation raWithValidation = readerAdapter as IDtdParserAdapterWithValidation;
if (raWithValidation != null) {
this.validate = raWithValidation.DtdValidation;
}
IDtdParserAdapterV1 raV1 = readerAdapter as IDtdParserAdapterV1;
if (raV1 != null) {
v1Compat = raV1.V1CompatibilityMode;
this.normalize = raV1.Normalization;
this.supportNamespaces = raV1.Namespaces;
}
#endif
schemaInfo = new SchemaInfo();
#if !SILVERLIGHT
schemaInfo.SchemaType = SchemaType.DTD;
#endif
stringBuilder = new BufferBuilder();
Uri baseUri = readerAdapter.BaseUri;
if (baseUri != null) {
documentBaseUri = baseUri.ToString();
}
freeFloatingDtd = false;
}
private void InitializeFreeFloatingDtd(string baseUri, string docTypeName, string publicId, string systemId, string internalSubset, IDtdParserAdapter adapter) {
Initialize(adapter);
if ( docTypeName == null || docTypeName.Length == 0 ) {
throw XmlConvert.CreateInvalidNameArgumentException( docTypeName, "docTypeName" );
}
// check doctype name
XmlConvert.VerifyName( docTypeName );
int colonPos = docTypeName.IndexOf( ':' );
if ( colonPos == -1 ) {
schemaInfo.DocTypeName = new XmlQualifiedName( nameTable.Add( docTypeName ) );
}
else {
schemaInfo.DocTypeName = new XmlQualifiedName( nameTable.Add( docTypeName.Substring( 0, colonPos ) ),
nameTable.Add( docTypeName.Substring( colonPos + 1 ) ) );
}
int i;
// check system id
if ( systemId != null && systemId.Length > 0 ) {
if ( ( i = xmlCharType.IsOnlyCharData( systemId ) ) >= 0 ) {
ThrowInvalidChar( curPos, systemId, i );
}
this.systemId = systemId;
}
// check public id
if ( publicId != null && publicId.Length > 0 ) {
if ( ( i = xmlCharType.IsPublicId( publicId ) ) >= 0 ) {
ThrowInvalidChar( curPos, publicId, i );
}
this.publicId = publicId;
}
// init free-floating internal subset
if ( internalSubset != null && internalSubset.Length > 0 ) {
readerAdapter.PushInternalDtd( baseUri, internalSubset );
hasFreeFloatingInternalSubset = true;
}
Uri baseUriOb = readerAdapter.BaseUri;
if ( baseUriOb != null ) {
documentBaseUri = baseUriOb.ToString();
}
freeFloatingDtd = true;
}
//
// IDtdParser interface
//
#region IDtdParser Members
IDtdInfo IDtdParser.ParseInternalDtd(IDtdParserAdapter adapter, bool saveInternalSubset) {
Initialize(adapter);
Parse(saveInternalSubset);
return schemaInfo;
}
IDtdInfo IDtdParser.ParseFreeFloatingDtd(string baseUri, string docTypeName, string publicId, string systemId, string internalSubset, IDtdParserAdapter adapter) {
InitializeFreeFloatingDtd(baseUri, docTypeName, publicId, systemId, internalSubset, adapter);
Parse(false);
return schemaInfo;
}
#endregion
//
// Private properties
//
private bool ParsingInternalSubset {
get {
return externalEntitiesDepth == 0;
}
}
private bool IgnoreEntityReferences {
get {
return scanningFunction == ScanningFunction.CondSection3;
}
}
private bool SaveInternalSubsetValue {
get {
return readerAdapter.EntityStackLength == 0 && internalSubsetValueSb != null;
}
}
private bool ParsingTopLevelMarkup {
get {
return scanningFunction == ScanningFunction.SubsetContent ||
( scanningFunction == ScanningFunction.ParamEntitySpace && savedScanningFunction == ScanningFunction.SubsetContent );
}
}
private bool SupportNamespaces {
get {
#if SILVERLIGHT
return true;
#else
return supportNamespaces;
#endif
}
}
private bool Normalize {
get {
#if SILVERLIGHT
return true;
#else
return normalize;
#endif
}
}
//
// Parsing methods
//
private void Parse( bool saveInternalSubset ) {
if ( freeFloatingDtd ) {
ParseFreeFloatingDtd();
}
else {
ParseInDocumentDtd( saveInternalSubset );
}
schemaInfo.Finish();
#if !SILVERLIGHT
// check undeclared forward references
if ( validate && undeclaredNotations != null ) {
foreach ( UndeclaredNotation un in undeclaredNotations.Values ) {
UndeclaredNotation tmpUn = un;
while ( tmpUn != null ) {
SendValidationEvent( XmlSeverityType.Error, new XmlSchemaException( Res.Sch_UndeclaredNotation, un.name, BaseUriStr, (int)un.lineNo, (int)un.linePos ) );
tmpUn = tmpUn.next;
}
}
}
#endif
}
private void ParseInDocumentDtd( bool saveInternalSubset ) {
LoadParsingBuffer();
scanningFunction = ScanningFunction.QName;
nextScaningFunction = ScanningFunction.Doctype1;
// doctype name
if ( GetToken( false ) != Token.QName ) {
OnUnexpectedError();
}
schemaInfo.DocTypeName = GetNameQualified( true );
// SYSTEM or PUBLIC id
Token token = GetToken( false );
if ( token == Token.SYSTEM || token == Token.PUBLIC ) {
ParseExternalId( token, Token.DOCTYPE, out publicId, out systemId );
token = GetToken( false);
}
switch ( token ) {
case Token.LeftBracket:
if ( saveInternalSubset ) {
SaveParsingBuffer(); // this will cause saving the internal subset right from the point after '['
internalSubsetValueSb = new BufferBuilder();
}
ParseInternalSubset();
break;
case Token.GreaterThan:
break;
default:
OnUnexpectedError();
break;
}
SaveParsingBuffer();
if ( systemId != null && systemId.Length > 0 ) {
ParseExternalSubset();
}
}
private void ParseFreeFloatingDtd() {
if ( hasFreeFloatingInternalSubset ) {
LoadParsingBuffer();
ParseInternalSubset();
SaveParsingBuffer();
}
if ( systemId != null && systemId.Length > 0 ) {
ParseExternalSubset();
}
}
private void ParseInternalSubset() {
Debug.Assert( ParsingInternalSubset );
ParseSubset();
}
private void ParseExternalSubset() {
Debug.Assert( externalEntitiesDepth == 0 );
// push external subset
if ( !readerAdapter.PushExternalSubset( systemId, publicId ) ) {
return;
}
Uri baseUri = readerAdapter.BaseUri;
if ( baseUri != null ) {
externalDtdBaseUri = baseUri.ToString();
}
externalEntitiesDepth++;
LoadParsingBuffer();
// parse
ParseSubset();
#if DEBUG
Debug.Assert( readerAdapter.EntityStackLength == 0 ||
( freeFloatingDtd && readerAdapter.EntityStackLength == 1 ) );
#endif
}
private void ParseSubset() {
int startTagEntityId;
for (;;) {
Token token = GetToken( false );
startTagEntityId = currentEntityId;
switch ( token ) {
case Token.AttlistDecl:
ParseAttlistDecl();
break;
case Token.ElementDecl:
ParseElementDecl();
break;
case Token.EntityDecl:
ParseEntityDecl();
break;
case Token.NotationDecl:
ParseNotationDecl();
break;
case Token.Comment:
ParseComment();
break;
case Token.PI:
ParsePI();
break;
case Token.CondSectionStart:
if ( ParsingInternalSubset ) {
Throw( curPos - 3, Res.Xml_InvalidConditionalSection ); // 3==strlen("<![")
}
ParseCondSection();
startTagEntityId = currentEntityId;
break;
case Token.CondSectionEnd:
if ( condSectionDepth > 0 ) {
condSectionDepth--;
#if !SILVERLIGHT
if ( validate && currentEntityId != condSectionEntityIds[condSectionDepth] ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
#endif
}
else {
Throw( curPos - 3, Res.Xml_UnexpectedCDataEnd );
}
break;
case Token.RightBracket:
if ( ParsingInternalSubset ) {
if ( condSectionDepth != 0 ) {
Throw( curPos, Res.Xml_UnclosedConditionalSection );
}
// append the rest to internal subset value but not the closing ']'
if ( internalSubsetValueSb != null ) {
Debug.Assert( curPos > 0 && chars[curPos-1] == ']' );
SaveParsingBuffer( curPos - 1 );
schemaInfo.InternalDtdSubset = internalSubsetValueSb.ToString();
internalSubsetValueSb = null;
}
// check '>'
if ( GetToken( false ) != Token.GreaterThan ) {
ThrowUnexpectedToken( curPos, ">" );
}
#if DEBUG
// check entity nesting
Debug.Assert( readerAdapter.EntityStackLength == 0 ||
( freeFloatingDtd && readerAdapter.EntityStackLength == 1 ) );
#endif
}
else {
Throw( curPos, Res.Xml_ExpectDtdMarkup );
}
return;
case Token.Eof:
if ( ParsingInternalSubset && !freeFloatingDtd ) {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
if ( condSectionDepth != 0 ) {
Throw( curPos, Res.Xml_UnclosedConditionalSection );
}
return;
default:
Debug.Assert( false );
break;
}
Debug.Assert( scanningFunction == ScanningFunction.SubsetContent );
if ( currentEntityId != startTagEntityId ) {
#if SILVERLIGHT
Throw(curPos, Res.Sch_ParEntityRefNesting);
#else
if ( validate ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
else {
if ( !v1Compat ) {
Throw( curPos, Res.Sch_ParEntityRefNesting );
}
}
#endif
}
}
}
private void ParseAttlistDecl() {
if (GetToken(true) != Token.QName) {
goto UnexpectedError;
}
// element name
XmlQualifiedName elementName = GetNameQualified(true);
SchemaElementDecl elementDecl;
if (!schemaInfo.ElementDecls.TryGetValue(elementName, out elementDecl)) {
if (!schemaInfo.UndeclaredElementDecls.TryGetValue(elementName, out elementDecl)) {
elementDecl = new SchemaElementDecl(elementName, elementName.Namespace);
schemaInfo.UndeclaredElementDecls.Add(elementName, elementDecl);
}
}
SchemaAttDef attrDef = null;
for (; ; ) {
switch (GetToken(false)) {
case Token.QName:
XmlQualifiedName attrName = GetNameQualified(true);
attrDef = new SchemaAttDef(attrName, attrName.Namespace);
attrDef.IsDeclaredInExternal = !ParsingInternalSubset;
attrDef.LineNumber = (int)LineNo;
attrDef.LinePosition = (int)LinePos - (curPos - tokenStartPos);
break;
case Token.GreaterThan:
#if !SILVERLIGHT
if ( v1Compat ) {
// check xml:space and xml:lang
//
if ( attrDef != null && attrDef.Prefix.Length > 0 && attrDef.Prefix.Equals( "xml" ) && attrDef.Name.Name == "space" ) {
attrDef.Reserved = SchemaAttDef.Reserve.XmlSpace;
if ( attrDef.Datatype.TokenizedType != XmlTokenizedType.ENUMERATION ) {
Throw( Res.Xml_EnumerationRequired, string.Empty, attrDef.LineNumber, attrDef.LinePosition );
}
if ( validate ) {
attrDef.CheckXmlSpace( readerAdapterWithValidation.ValidationEventHandling );
}
}
}
#endif
return;
default:
goto UnexpectedError;
}
bool attrDefAlreadyExists = (elementDecl.GetAttDef(attrDef.Name) != null);
ParseAttlistType(attrDef, elementDecl, attrDefAlreadyExists);
ParseAttlistDefault(attrDef, attrDefAlreadyExists);
// check xml:space and xml:lang
if (attrDef.Prefix.Length > 0 && attrDef.Prefix.Equals("xml")) {
if ( attrDef.Name.Name == "space" ) {
#if !SILVERLIGHT
if ( v1Compat ) {
//
string val = attrDef.DefaultValueExpanded.Trim();
if ( val.Equals( "preserve" ) || val.Equals( "default" ) ) {
attrDef.Reserved = SchemaAttDef.Reserve.XmlSpace;
}
}
else {
#endif
attrDef.Reserved = SchemaAttDef.Reserve.XmlSpace;
if ( attrDef.TokenizedType != XmlTokenizedType.ENUMERATION ) {
Throw( Res.Xml_EnumerationRequired, string.Empty, attrDef.LineNumber, attrDef.LinePosition );
}
#if !SILVERLIGHT
if ( validate ) {
attrDef.CheckXmlSpace( readerAdapterWithValidation.ValidationEventHandling );
}
}
#endif
}
else if ( attrDef.Name.Name == "lang" ) {
attrDef.Reserved = SchemaAttDef.Reserve.XmlLang;
}
}
// add attribute to element decl
if (!attrDefAlreadyExists) {
elementDecl.AddAttDef(attrDef);
}
}
UnexpectedError:
OnUnexpectedError();
}
private void ParseAttlistType( SchemaAttDef attrDef, SchemaElementDecl elementDecl, bool ignoreErrors ) {
Token token = GetToken( true );
if ( token != Token.CDATA ) {
elementDecl.HasNonCDataAttribute = true;
}
if ( IsAttributeValueType( token ) ) {
attrDef.TokenizedType = (XmlTokenizedType)(int)token;
#if !SILVERLIGHT
attrDef.SchemaType = XmlSchemaType.GetBuiltInSimpleType( attrDef.Datatype.TypeCode );
#endif
switch ( token ) {
case Token.NOTATION:
break;
case Token.ID:
#if !SILVERLIGHT
if ( validate && elementDecl.IsIdDeclared ) {
SchemaAttDef idAttrDef = elementDecl.GetAttDef( attrDef.Name );
if ( ( idAttrDef == null || idAttrDef.Datatype.TokenizedType != XmlTokenizedType.ID ) && !ignoreErrors ) {
SendValidationEvent( XmlSeverityType.Error, Res.Sch_IdAttrDeclared, elementDecl.Name.ToString() );
}
}
#endif
elementDecl.IsIdDeclared = true;
return;
default:
return;
}
#if !SILVERLIGHT
// check notation constrains
if ( validate ) {
if (elementDecl.IsNotationDeclared && !ignoreErrors ) {
SendValidationEvent( curPos - 8, XmlSeverityType.Error, Res.Sch_DupNotationAttribute, elementDecl.Name.ToString() ); // 8 == strlen("NOTATION")
}
else {
if ( elementDecl.ContentValidator != null &&
elementDecl.ContentValidator.ContentType == XmlSchemaContentType.Empty &&
!ignoreErrors ) {
SendValidationEvent( curPos - 8, XmlSeverityType.Error, Res.Sch_NotationAttributeOnEmptyElement, elementDecl.Name.ToString() );// 8 == strlen("NOTATION")
}
elementDecl.IsNotationDeclared = true;
}
}
#endif
if ( GetToken( true ) != Token.LeftParen ) {
goto UnexpectedError;
}
// parse notation list
if ( GetToken( false ) != Token.Name ) {
goto UnexpectedError;
}
for (;;) {
string notationName = GetNameString();
#if !SILVERLIGHT
if ( !schemaInfo.Notations.ContainsKey(notationName) ) {
AddUndeclaredNotation(notationName);
}
if ( validate && !v1Compat && attrDef.Values != null && attrDef.Values.Contains( notationName ) && !ignoreErrors ) {
SendValidationEvent( XmlSeverityType.Error, new XmlSchemaException( Res.Xml_AttlistDuplNotationValue, notationName, BaseUriStr, (int)LineNo, (int)LinePos ) );
}
attrDef.AddValue( notationName );
#endif
switch ( GetToken( false ) ) {
case Token.Or:
if ( GetToken( false ) != Token.Name ) {
goto UnexpectedError;
}
continue;
case Token.RightParen:
return;
default:
goto UnexpectedError;
}
}
}
else if ( token == Token.LeftParen ) {
attrDef.TokenizedType = XmlTokenizedType.ENUMERATION;
#if !SILVERLIGHT
attrDef.SchemaType = XmlSchemaType.GetBuiltInSimpleType( attrDef.Datatype.TypeCode );
#endif
// parse nmtoken list
if ( GetToken( false ) != Token.Nmtoken )
goto UnexpectedError;
#if !SILVERLIGHT
attrDef.AddValue( GetNameString() );
#endif
for (;;) {
switch ( GetToken( false ) ) {
case Token.Or:
if ( GetToken( false ) != Token.Nmtoken )
goto UnexpectedError;
string nmtoken = GetNmtokenString();
#if !SILVERLIGHT
if ( validate && !v1Compat && attrDef.Values != null && attrDef.Values.Contains( nmtoken ) && !ignoreErrors ) {
SendValidationEvent( XmlSeverityType.Error, new XmlSchemaException( Res.Xml_AttlistDuplEnumValue, nmtoken, BaseUriStr, (int)LineNo, (int)LinePos ) );
}
attrDef.AddValue( nmtoken );
#endif
break;
case Token.RightParen:
return;
default:
goto UnexpectedError;
}
}
}
else {
goto UnexpectedError;
}
UnexpectedError:
OnUnexpectedError();
}
private void ParseAttlistDefault( SchemaAttDef attrDef, bool ignoreErrors ) {
switch ( GetToken( true ) ) {
case Token.REQUIRED:
attrDef.Presence = SchemaDeclBase.Use.Required;
return;
case Token.IMPLIED:
attrDef.Presence = SchemaDeclBase.Use.Implied;
return;
case Token.FIXED:
attrDef.Presence = SchemaDeclBase.Use.Fixed;
if ( GetToken( true ) != Token.Literal ) {
goto UnexpectedError;
}
break;
case Token.Literal:
break;
default:
goto UnexpectedError;
}
#if !SILVERLIGHT
if ( validate && attrDef.Datatype.TokenizedType == XmlTokenizedType.ID&& !ignoreErrors ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_AttListPresence, string.Empty );
}
#endif
if ( attrDef.TokenizedType != XmlTokenizedType.CDATA ) {
// non-CDATA attribute type normalization - strip spaces
attrDef.DefaultValueExpanded = GetValueWithStrippedSpaces();
}
else {
attrDef.DefaultValueExpanded = GetValue();
}
attrDef.ValueLineNumber = (int)literalLineInfo.lineNo;
attrDef.ValueLinePosition = (int)literalLineInfo.linePos + 1;
#if !SILVERLIGHT
DtdValidator.SetDefaultTypedValue( attrDef, readerAdapter );
#endif
return;
UnexpectedError:
OnUnexpectedError();
}
private void ParseElementDecl() {
// element name
if ( GetToken( true ) != Token.QName ) {
goto UnexpectedError;
}
// get schema decl for element
SchemaElementDecl elementDecl = null;
XmlQualifiedName name = GetNameQualified( true );
if (schemaInfo.ElementDecls.TryGetValue(name, out elementDecl)) {
#if !SILVERLIGHT
if ( validate ) {
SendValidationEvent( curPos - name.Name.Length, XmlSeverityType.Error, Res.Sch_DupElementDecl, GetNameString() );
}
#endif
}
else {
if ( schemaInfo.UndeclaredElementDecls.TryGetValue(name, out elementDecl ) ) {
schemaInfo.UndeclaredElementDecls.Remove( name );
}
else {
elementDecl = new SchemaElementDecl( name, name.Namespace );
}
schemaInfo.ElementDecls.Add( name, elementDecl );
}
elementDecl.IsDeclaredInExternal = !ParsingInternalSubset;
// content spec
#if SILVERLIGHT
switch ( GetToken( true ) ) {
case Token.EMPTY:
case Token.ANY:
break;
case Token.LeftParen:
switch ( GetToken( false ) ) {
case Token.PCDATA:
ParseElementMixedContentNoValidation();
break;
case Token.None:
ParseElementOnlyContentNoValidation();
break;
default:
goto UnexpectedError;
}
break;
default:
goto UnexpectedError;
}
#else
switch ( GetToken( true ) ) {
case Token.EMPTY:
elementDecl.ContentValidator = ContentValidator.Empty;
break;
case Token.ANY:
elementDecl.ContentValidator = ContentValidator.Any;
break;
case Token.LeftParen:
int startParenEntityId = currentEntityId;
switch ( GetToken( false ) ) {
case Token.PCDATA:
{
ParticleContentValidator pcv = new ParticleContentValidator( XmlSchemaContentType.Mixed );
pcv.Start();
pcv.OpenGroup();
ParseElementMixedContent( pcv, startParenEntityId );
elementDecl.ContentValidator = pcv.Finish( true );
break;
}
case Token.None:
{
ParticleContentValidator pcv = null;
pcv = new ParticleContentValidator( XmlSchemaContentType.ElementOnly );
pcv.Start();
pcv.OpenGroup();
ParseElementOnlyContent( pcv, startParenEntityId );
elementDecl.ContentValidator = pcv.Finish( true );
break;
}
default:
goto UnexpectedError;
}
break;
default:
goto UnexpectedError;
}
#endif
if ( GetToken( false ) != Token.GreaterThan ) {
ThrowUnexpectedToken( curPos, ">" );
}
return;
UnexpectedError:
OnUnexpectedError();
}
#if SILVERLIGHT // Element content model parsing methods without validation
private class ParseElementOnlyContentNoValidation_LocalFrame
{
public ParseElementOnlyContentNoValidation_LocalFrame() {
parsingSchema = Token.None;
}
public Token parsingSchema;
}
private void ParseElementOnlyContentNoValidation() {
Stack<ParseElementOnlyContentNoValidation_LocalFrame> localFrames =
new Stack<ParseElementOnlyContentNoValidation_LocalFrame>();
ParseElementOnlyContentNoValidation_LocalFrame currentFrame =
new ParseElementOnlyContentNoValidation_LocalFrame();
localFrames.Push(currentFrame);
RecursiveCall:
Loop:
switch ( GetToken( false ) ) {
case Token.QName:
GetNameQualified(true);
ParseHowManyNoValidation();
break;
case Token.LeftParen:
// We could just do this:
// ParseElementOnlyContentNoValidation();
//
// But that would be a recursion - so we will simulate the call using our localFrames stack
// instead.
currentFrame =
new ParseElementOnlyContentNoValidation_LocalFrame();
localFrames.Push(currentFrame);
goto RecursiveCall;
// And we should return here when we return from the recursion
// but it's the samea s returning after the switch statement
case Token.GreaterThan:
Throw( curPos, Res.Xml_InvalidContentModel );
goto Return;
default:
goto UnexpectedError;
}
ReturnFromRecursiveCall:
switch ( GetToken( false ) ) {
case Token.Comma:
if ( currentFrame.parsingSchema == Token.Or ) {
Throw( curPos, Res.Xml_InvalidContentModel );
}
currentFrame.parsingSchema = Token.Comma;
break;
case Token.Or:
if ( currentFrame.parsingSchema == Token.Comma ) {
Throw( curPos, Res.Xml_InvalidContentModel );
}
currentFrame.parsingSchema = Token.Or;
break;
case Token.RightParen:
ParseHowManyNoValidation();
goto Return;
case Token.GreaterThan:
Throw( curPos, Res.Xml_InvalidContentModel );
goto Return;
default:
goto UnexpectedError;
}
goto Loop;
UnexpectedError:
OnUnexpectedError();
Return:
// This is equivalent to return; statement
// we simulate it using our localFrames stack
localFrames.Pop();
if (localFrames.Count > 0) {
currentFrame = localFrames.Peek();
goto ReturnFromRecursiveCall;
}
else {
return;
}
}
private void ParseHowManyNoValidation() {
GetToken( false );
}
private void ParseElementMixedContentNoValidation() {
bool hasNames = false;
for (;;) {
switch ( GetToken( false ) ) {
case Token.RightParen:
if ( GetToken( false ) != Token.Star && hasNames ) {
ThrowUnexpectedToken( curPos, "*" );
}
return;
case Token.Or:
if ( !hasNames ) {
hasNames = true;
}
if ( GetToken( false ) != Token.QName ) {
goto default;
}
GetNameQualified( true );
continue;
default:
OnUnexpectedError();
break;
}
}
}
#else // Element content model parsing methods with validation support
private class ParseElementOnlyContent_LocalFrame
{
public ParseElementOnlyContent_LocalFrame(int startParentEntityIdParam) {
startParenEntityId = startParentEntityIdParam;
parsingSchema = Token.None;
}
// pcv doesn't need to be stored for each frame as it never changes
public int startParenEntityId;
public Token parsingSchema;
}
private void ParseElementOnlyContent( ParticleContentValidator pcv, int startParenEntityId ) {
Stack<ParseElementOnlyContent_LocalFrame> localFrames = new Stack<ParseElementOnlyContent_LocalFrame>();
ParseElementOnlyContent_LocalFrame currentFrame = new ParseElementOnlyContent_LocalFrame(startParenEntityId);
localFrames.Push(currentFrame);
RecursiveCall:
Loop:
switch ( GetToken( false ) ) {
case Token.QName:
pcv.AddName( GetNameQualified(true), null );
ParseHowMany( pcv );
break;
case Token.LeftParen:
pcv.OpenGroup();
// We could just do this:
// ParseElementOnlyContent( pcv, currentEntityId );
//
// But that would be recursion - so we will simulate the call using our localFrames stack
// instead.
currentFrame =
new ParseElementOnlyContent_LocalFrame(currentEntityId);
localFrames.Push(currentFrame);
goto RecursiveCall;
// And we should return here when we return from recursion call
// but it's the same as returning after the switch statement
case Token.GreaterThan:
Throw( curPos, Res.Xml_InvalidContentModel );
goto Return;
default:
goto UnexpectedError;
}
ReturnFromRecursiveCall:
switch ( GetToken( false ) ) {
case Token.Comma:
if ( currentFrame.parsingSchema == Token.Or ) {
Throw( curPos, Res.Xml_InvalidContentModel );
}
pcv.AddSequence();
currentFrame.parsingSchema = Token.Comma;
break;
case Token.Or:
if (currentFrame.parsingSchema == Token.Comma) {
Throw( curPos, Res.Xml_InvalidContentModel );
}
pcv.AddChoice();
currentFrame.parsingSchema = Token.Or;
break;
case Token.RightParen:
pcv.CloseGroup();
if (validate && currentEntityId != currentFrame.startParenEntityId) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
ParseHowMany( pcv );
goto Return;
case Token.GreaterThan:
Throw( curPos, Res.Xml_InvalidContentModel );
goto Return;
default:
goto UnexpectedError;
}
goto Loop;
UnexpectedError:
OnUnexpectedError();
Return:
// This is equivalent to return; statement
// we simlate it using our localFrames stack
localFrames.Pop();
if (localFrames.Count > 0) {
currentFrame = (ParseElementOnlyContent_LocalFrame)localFrames.Peek();
goto ReturnFromRecursiveCall;
}
else {
return;
}
}
private void ParseHowMany( ParticleContentValidator pcv ) {
switch ( GetToken( false ) ) {
case Token.Star:
pcv.AddStar();
return;
case Token.QMark:
pcv.AddQMark();
return;
case Token.Plus:
pcv.AddPlus();
return;
default:
return;
}
}
private void ParseElementMixedContent( ParticleContentValidator pcv, int startParenEntityId ) {
bool hasNames = false;
int connectorEntityId = -1;
int contentEntityId = currentEntityId;
for (;;) {
switch ( GetToken( false ) ) {
case Token.RightParen:
pcv.CloseGroup();
if ( validate && currentEntityId != startParenEntityId ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
if ( GetToken( false ) == Token.Star && hasNames ) {
pcv.AddStar();
}
else if ( hasNames ) {
ThrowUnexpectedToken( curPos, "*" );
}
return;
case Token.Or:
if ( !hasNames ) {
hasNames = true;
}
else {
pcv.AddChoice();
}
if ( validate ) {
connectorEntityId = currentEntityId;
if ( contentEntityId < connectorEntityId ) { // entity repl.text starting with connector
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
}
if ( GetToken( false ) != Token.QName ) {
goto default;
}
XmlQualifiedName name = GetNameQualified( true );
if ( pcv.Exists( name ) && validate ) {
SendValidationEvent( XmlSeverityType.Error, Res.Sch_DupElement, name.ToString() );
}
pcv.AddName( name, null );
if ( validate ) {
contentEntityId = currentEntityId;
if ( contentEntityId < connectorEntityId ) { // entity repl.text ending with connector
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
}
continue;
default:
OnUnexpectedError();
break;
}
}
}
#endif // Element content model parsing methods with validation support
private void ParseEntityDecl() {
bool isParamEntity = false;
SchemaEntity entity = null;
// get entity name and type
switch ( GetToken( true ) ) {
case Token.Percent:
isParamEntity = true;
if ( GetToken( true ) != Token.Name ) {
goto UnexpectedError;
}
goto case Token.Name;
case Token.Name:
// create entity object
XmlQualifiedName entityName = GetNameQualified( false );
entity = new SchemaEntity( entityName, isParamEntity );
entity.BaseURI = BaseUriStr;
entity.DeclaredURI = ( externalDtdBaseUri.Length == 0 ) ? documentBaseUri : externalDtdBaseUri;
if ( isParamEntity ) {
if ( !schemaInfo.ParameterEntities.ContainsKey( entityName ) ) {
schemaInfo.ParameterEntities.Add( entityName, entity );
}
}
else {
if ( !schemaInfo.GeneralEntities.ContainsKey( entityName ) ) {
schemaInfo.GeneralEntities.Add( entityName, entity );
}
}
entity.DeclaredInExternal = !ParsingInternalSubset;
entity.ParsingInProgress = true;
break;
default:
goto UnexpectedError;
}
Token token = GetToken( true );
switch ( token ) {
case Token.PUBLIC:
case Token.SYSTEM:
string systemId;
string publicId;
ParseExternalId( token, Token.EntityDecl, out publicId, out systemId );
entity.IsExternal = true;
entity.Url = systemId;
entity.Pubid = publicId;
if ( GetToken( false ) == Token.NData ) {
if ( isParamEntity ) {
ThrowUnexpectedToken( curPos - 5, ">" ); // 5 == strlen("NDATA")
}
if ( !whitespaceSeen ) {
Throw( curPos - 5, Res.Xml_ExpectingWhiteSpace, "NDATA" );
}
if ( GetToken( true ) != Token.Name ) {
goto UnexpectedError;
}
entity.NData = GetNameQualified( false );
#if !SILVERLIGHT
string notationName = entity.NData.Name;
if ( !schemaInfo.Notations.ContainsKey(notationName) ) {
AddUndeclaredNotation(notationName);
}
#endif
}
break;
case Token.Literal:
entity.Text = GetValue();
entity.Line = (int)literalLineInfo.lineNo;
entity.Pos = (int)literalLineInfo.linePos;
break;
default:
goto UnexpectedError;
}
if ( GetToken( false ) == Token.GreaterThan ) {
entity.ParsingInProgress = false;
return;
}
UnexpectedError:
OnUnexpectedError();
}
private void ParseNotationDecl() {
// notation name
if ( GetToken( true ) != Token.Name ) {
OnUnexpectedError();
}
XmlQualifiedName notationName = GetNameQualified( false );
#if !SILVERLIGHT
SchemaNotation notation = null;
if ( !schemaInfo.Notations.ContainsKey( notationName.Name ) ) {
if ( undeclaredNotations != null ) {
undeclaredNotations.Remove( notationName.Name );
}
notation = new SchemaNotation( notationName );
schemaInfo.Notations.Add(notation.Name.Name, notation);
}
else {
// duplicate notation
if ( validate ) {
SendValidationEvent( curPos - notationName.Name.Length, XmlSeverityType.Error, Res.Sch_DupNotation, notationName.Name );
}
}
#endif
// public / system id
Token token = GetToken( true );
if ( token == Token.SYSTEM || token == Token.PUBLIC ) {
string notationPublicId, notationSystemId;
ParseExternalId( token, Token.NOTATION, out notationPublicId, out notationSystemId );
#if !SILVERLIGHT
if ( notation != null ) {
notation.SystemLiteral = notationSystemId;
notation.Pubid = notationPublicId;
}
#endif
}
else {
OnUnexpectedError();
}
if ( GetToken( false ) != Token.GreaterThan )
OnUnexpectedError();
}
#if !SILVERLIGHT
private void AddUndeclaredNotation(string notationName) {
if (undeclaredNotations == null) {
undeclaredNotations = new Dictionary<string, UndeclaredNotation>();
}
UndeclaredNotation un = new UndeclaredNotation(notationName, LineNo, LinePos - notationName.Length);
UndeclaredNotation loggedUn;
if (undeclaredNotations.TryGetValue(notationName, out loggedUn)) {
un.next = loggedUn.next;
loggedUn.next = un;
}
else {
undeclaredNotations.Add(notationName, un);
}
}
#endif
private void ParseComment() {
SaveParsingBuffer();
#if !SILVERLIGHT
try {
#endif
if ( SaveInternalSubsetValue ) {
readerAdapter.ParseComment( internalSubsetValueSb );
internalSubsetValueSb.Append( "-->" );
}
else {
readerAdapter.ParseComment( null );
}
#if !SILVERLIGHT
}
catch (XmlException e) {
if ( e.ResString == Res.Xml_UnexpectedEOF && currentEntityId != 0 ) {
SendValidationEvent( XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, null );
}
else {
throw;
}
}
#endif
LoadParsingBuffer();
}
private void ParsePI() {
SaveParsingBuffer();
if ( SaveInternalSubsetValue ) {
readerAdapter.ParsePI( internalSubsetValueSb );
internalSubsetValueSb.Append( "?>" );
}
else {
readerAdapter.ParsePI( null );
}
LoadParsingBuffer();
}
private void ParseCondSection() {
int csEntityId = currentEntityId;
switch ( GetToken( false ) ) {
case Token.INCLUDE:
if ( GetToken( false ) != Token.LeftBracket ) {
goto default;
}
#if !SILVERLIGHT
if ( validate && csEntityId != currentEntityId ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
if ( validate ) {
if ( condSectionEntityIds == null ) {
condSectionEntityIds = new int[CondSectionEntityIdsInitialSize];
}
else if ( condSectionEntityIds.Length == condSectionDepth ) {
int[] tmp = new int[condSectionEntityIds.Length*2];
Array.Copy( condSectionEntityIds, 0, tmp, 0, condSectionEntityIds.Length );
condSectionEntityIds = tmp;
}
condSectionEntityIds[condSectionDepth] = csEntityId;
}
#endif
condSectionDepth++;
break;
case Token.IGNORE:
if ( GetToken( false ) != Token.LeftBracket ) {
goto default;
}
#if !SILVERLIGHT
if ( validate && csEntityId != currentEntityId ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
#endif
// the content of the ignore section is parsed & skipped by scanning function
if ( GetToken( false ) != Token.CondSectionEnd ) {
goto default;
}
#if !SILVERLIGHT
if ( validate && csEntityId != currentEntityId ) {
SendValidationEvent( curPos, XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, string.Empty );
}
#endif
break;
default:
OnUnexpectedError();
break;
}
}
private void ParseExternalId( Token idTokenType, Token declType, out string publicId, out string systemId ) {
LineInfo keywordLineInfo = new LineInfo( LineNo, LinePos - 6 );
publicId = null;
systemId = null;
if ( GetToken( true ) != Token.Literal ) {
ThrowUnexpectedToken( curPos, "\"", "'" );
}
if ( idTokenType == Token.SYSTEM ) {
systemId = GetValue();
if ( systemId.IndexOf( '#' ) >= 0 ) {
Throw( curPos - systemId.Length - 1, Res.Xml_FragmentId, new string[] { systemId.Substring( systemId.IndexOf( '#' ) ), systemId } );
}
if ( declType == Token.DOCTYPE && !freeFloatingDtd ) {
literalLineInfo.linePos++;
readerAdapter.OnSystemId( systemId, keywordLineInfo, literalLineInfo );
}
}
else {
Debug.Assert( idTokenType == Token.PUBLIC );
publicId = GetValue();
// verify if it contains chars valid for public ids
int i;
if ( ( i = xmlCharType.IsPublicId( publicId ) ) >= 0 ) {
ThrowInvalidChar( curPos - 1 - publicId.Length + i, publicId, i );
}
if ( declType == Token.DOCTYPE && !freeFloatingDtd ) {
literalLineInfo.linePos++;
readerAdapter.OnPublicId( publicId, keywordLineInfo, literalLineInfo );
if ( GetToken( false ) == Token.Literal ) {
if ( !whitespaceSeen ) {
Throw( Res.Xml_ExpectingWhiteSpace, new string( literalQuoteChar, 1 ), (int)literalLineInfo.lineNo, (int)literalLineInfo.linePos );
}
systemId = GetValue();
literalLineInfo.linePos++;
readerAdapter.OnSystemId( systemId, keywordLineInfo, literalLineInfo );
}
else {
ThrowUnexpectedToken( curPos, "\"", "'" );
}
}
else {
if ( GetToken( false ) == Token.Literal ) {
if ( !whitespaceSeen ) {
Throw( Res.Xml_ExpectingWhiteSpace, new string( literalQuoteChar, 1 ), (int)literalLineInfo.lineNo, (int)literalLineInfo.linePos );
}
systemId = GetValue();
}
else if ( declType != Token.NOTATION ) {
ThrowUnexpectedToken( curPos, "\"", "'" );
}
}
}
}
//
// Scanning methods - works directly with parsing buffer
//
private Token GetToken( bool needWhiteSpace ) {
whitespaceSeen = false;
for (;;) {
switch ( chars[curPos] ) {
case (char)0:
if ( curPos == charsUsed ) {
goto ReadData;
}
else {
ThrowInvalidChar( chars, charsUsed, curPos );
}
break;
case (char)0xA:
whitespaceSeen = true;
curPos++;
readerAdapter.OnNewLine( curPos );
continue;
case (char)0xD:
whitespaceSeen = true;
if ( chars[curPos+1] == (char)0xA ) {
if ( Normalize ) {
SaveParsingBuffer(); // EOL normalization of 0xD 0xA
readerAdapter.CurrentPosition++;
}
curPos += 2;
}
else if ( curPos+1 < charsUsed || readerAdapter.IsEof ) {
chars[curPos] = (char)0xA; // EOL normalization of 0xD
curPos++;
}
else {
goto ReadData;
}
readerAdapter.OnNewLine( curPos );
continue;
case (char)0x9:
case (char)0x20:
whitespaceSeen = true;
curPos++;
continue;
case '%':
if ( charsUsed - curPos < 2 ) {
goto ReadData;
}
if ( !xmlCharType.IsWhiteSpace( chars[curPos+1] ) ) {
if ( IgnoreEntityReferences ) {
curPos++;
}
else {
HandleEntityReference( true, false, false );
}
continue;
}
goto default;
default:
if ( needWhiteSpace && !whitespaceSeen && scanningFunction != ScanningFunction.ParamEntitySpace ) {
Throw( curPos, Res.Xml_ExpectingWhiteSpace, ParseUnexpectedToken( curPos ) );
}
tokenStartPos = curPos;
SwitchAgain:
switch ( scanningFunction ) {
case ScanningFunction.Name: return ScanNameExpected();
case ScanningFunction.QName: return ScanQNameExpected();
case ScanningFunction.Nmtoken: return ScanNmtokenExpected();
case ScanningFunction.SubsetContent: return ScanSubsetContent();
case ScanningFunction.Doctype1: return ScanDoctype1();
case ScanningFunction.Doctype2: return ScanDoctype2();
case ScanningFunction.Element1: return ScanElement1();
case ScanningFunction.Element2: return ScanElement2();
case ScanningFunction.Element3: return ScanElement3();
case ScanningFunction.Element4: return ScanElement4();
case ScanningFunction.Element5: return ScanElement5();
case ScanningFunction.Element6: return ScanElement6();
case ScanningFunction.Element7: return ScanElement7();
case ScanningFunction.Attlist1: return ScanAttlist1();
case ScanningFunction.Attlist2: return ScanAttlist2();
case ScanningFunction.Attlist3: return ScanAttlist3();
case ScanningFunction.Attlist4: return ScanAttlist4();
case ScanningFunction.Attlist5: return ScanAttlist5();
case ScanningFunction.Attlist6: return ScanAttlist6();
case ScanningFunction.Attlist7: return ScanAttlist7();
case ScanningFunction.Notation1: return ScanNotation1();
case ScanningFunction.SystemId: return ScanSystemId();
case ScanningFunction.PublicId1: return ScanPublicId1();
case ScanningFunction.PublicId2: return ScanPublicId2();
case ScanningFunction.Entity1: return ScanEntity1();
case ScanningFunction.Entity2: return ScanEntity2();
case ScanningFunction.Entity3: return ScanEntity3();
case ScanningFunction.CondSection1: return ScanCondSection1();
case ScanningFunction.CondSection2: return ScanCondSection2();
case ScanningFunction.CondSection3: return ScanCondSection3();
case ScanningFunction.ClosingTag: return ScanClosingTag();
case ScanningFunction.ParamEntitySpace:
whitespaceSeen = true;
scanningFunction = savedScanningFunction;
goto SwitchAgain;
default:
Debug.Assert( false );
return Token.None;
}
}
ReadData:
if ( readerAdapter.IsEof || ReadData() == 0 ) {
if ( HandleEntityEnd( false ) ) {
continue;
}
if ( scanningFunction == ScanningFunction.SubsetContent ) {
return Token.Eof;
}
else {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
}
}
}
private Token ScanSubsetContent() {
for (;;) {
switch ( chars[curPos] ) {
case '<':
switch ( chars[curPos+1] ) {
case '!':
switch ( chars[curPos+2] ) {
case 'E':
if ( chars[curPos+3] == 'L' ) {
if ( charsUsed - curPos < 9 ) {
goto ReadData;
}
if ( chars[curPos+4] != 'E' || chars[curPos+5] != 'M' ||
chars[curPos+6] != 'E' || chars[curPos+7] != 'N' ||
chars[curPos+8] != 'T' ) {
Throw( curPos, Res.Xml_ExpectDtdMarkup );
}
curPos += 9;
scanningFunction = ScanningFunction.QName;
nextScaningFunction = ScanningFunction.Element1;
return Token.ElementDecl;
}
else if ( chars[curPos+3] == 'N' ) {
if ( charsUsed - curPos < 8 ) {
goto ReadData;
}
if ( chars[curPos+4] != 'T' || chars[curPos+5] != 'I' ||
chars[curPos+6] != 'T' || chars[curPos+7] != 'Y' ) {
Throw( curPos, Res.Xml_ExpectDtdMarkup );
}
curPos += 8;
scanningFunction = ScanningFunction.Entity1;
return Token.EntityDecl;
}
else {
if ( charsUsed - curPos < 4 ) {
goto ReadData;
}
Throw( curPos, Res.Xml_ExpectDtdMarkup );
return Token.None;
}
case 'A':
if ( charsUsed - curPos < 9 ) {
goto ReadData;
}
if ( chars[curPos+3] != 'T' || chars[curPos+4] != 'T' ||
chars[curPos+5] != 'L' || chars[curPos+6] != 'I' ||
chars[curPos+7] != 'S' || chars[curPos+8] != 'T' ) {
Throw( curPos, Res.Xml_ExpectDtdMarkup );
}
curPos += 9;
scanningFunction = ScanningFunction.QName;
nextScaningFunction = ScanningFunction.Attlist1;
return Token.AttlistDecl;
case 'N':
if ( charsUsed - curPos < 10 ) {
goto ReadData;
}
if ( chars[curPos+3] != 'O' || chars[curPos+4] != 'T' ||
chars[curPos+5] != 'A' || chars[curPos+6] != 'T' ||
chars[curPos+7] != 'I' || chars[curPos+8] != 'O' ||
chars[curPos+9] != 'N' ) {
Throw( curPos, Res.Xml_ExpectDtdMarkup );
}
curPos += 10;
scanningFunction = ScanningFunction.Name;
nextScaningFunction = ScanningFunction.Notation1;
return Token.NotationDecl;
case '[':
curPos += 3;
scanningFunction = ScanningFunction.CondSection1;
return Token.CondSectionStart;
case '-':
if ( chars[curPos+3] == '-' ) {
curPos += 4;
return Token.Comment;
}
else {
if ( charsUsed - curPos < 4 ) {
goto ReadData;
}
Throw( curPos, Res.Xml_ExpectDtdMarkup );
break;
}
default:
if ( charsUsed - curPos < 3 ) {
goto ReadData;
}
Throw( curPos + 2, Res.Xml_ExpectDtdMarkup );
break;
}
break;
case '?':
curPos += 2;
return Token.PI;
default:
if ( charsUsed - curPos < 2 ) {
goto ReadData;
}
Throw( curPos, Res.Xml_ExpectDtdMarkup );
return Token.None;
}
break;
case ']':
if ( charsUsed - curPos < 2 && !readerAdapter.IsEof ) {
goto ReadData;
}
if ( chars[curPos+1] != ']' ) {
curPos++;
scanningFunction = ScanningFunction.ClosingTag;
return Token.RightBracket;
}
if ( charsUsed - curPos < 3 && !readerAdapter.IsEof ) {
goto ReadData;
}
if ( chars[curPos+1] == ']' && chars[curPos+2] == '>' ) {
curPos += 3;
return Token.CondSectionEnd;
}
goto default;
default:
if ( charsUsed - curPos == 0 ) {
goto ReadData;
}
Throw( curPos, Res.Xml_ExpectDtdMarkup );
break;
}
ReadData:
if ( ReadData() == 0 ) {
Throw( charsUsed, Res.Xml_IncompleteDtdContent );
}
}
}
private Token ScanNameExpected() {
ScanName();
scanningFunction = nextScaningFunction;
return Token.Name;
}
private Token ScanQNameExpected() {
ScanQName();
scanningFunction = nextScaningFunction;
return Token.QName;
}
private Token ScanNmtokenExpected() {
ScanNmtoken();
scanningFunction = nextScaningFunction;
return Token.Nmtoken;
}
private Token ScanDoctype1() {
switch ( chars[curPos] ) {
case 'P':
if ( !EatPublicKeyword() ) {
Throw( curPos, Res.Xml_ExpectExternalOrClose );
}
nextScaningFunction = ScanningFunction.Doctype2;
scanningFunction = ScanningFunction.PublicId1;
return Token.PUBLIC;
case 'S':
if ( !EatSystemKeyword() ) {
Throw( curPos, Res.Xml_ExpectExternalOrClose );
}
nextScaningFunction = ScanningFunction.Doctype2;
scanningFunction = ScanningFunction.SystemId;
return Token.SYSTEM;
case '[':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.LeftBracket;
case '>':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.GreaterThan;
default:
Throw( curPos, Res.Xml_ExpectExternalOrClose );
return Token.None;
}
}
private Token ScanDoctype2() {
switch ( chars[curPos] ) {
case '[':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.LeftBracket;
case '>':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.GreaterThan;
default:
Throw( curPos, Res.Xml_ExpectSubOrClose );
return Token.None;
}
}
private Token ScanClosingTag() {
if ( chars[curPos] != '>' ) {
ThrowUnexpectedToken( curPos, ">" );
}
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.GreaterThan;
}
private Token ScanElement1() {
for (;;) {
switch ( chars[curPos] ) {
case '(':
scanningFunction = ScanningFunction.Element2;
curPos++;
return Token.LeftParen;
case 'E':
if ( charsUsed - curPos < 5 ) {
goto ReadData;
}
if ( chars[curPos+1] == 'M' && chars[curPos+2] == 'P' &&
chars[curPos+3] == 'T' && chars[curPos+4] == 'Y' ) {
curPos += 5;
scanningFunction = ScanningFunction.ClosingTag;
return Token.EMPTY;
}
goto default;
case 'A':
if ( charsUsed - curPos < 3 ) {
goto ReadData;
}
if ( chars[curPos+1] == 'N' && chars[curPos+2] == 'Y' ) {
curPos += 3;
scanningFunction = ScanningFunction.ClosingTag;
return Token.ANY;
}
goto default;
default:
Throw( curPos, Res.Xml_InvalidContentModel );
break;
}
ReadData:
if ( ReadData() == 0 ) {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
}
}
private Token ScanElement2() {
if ( chars[curPos] == '#' ) {
while ( charsUsed - curPos < 7 ) {
if ( ReadData() == 0 ) {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
}
if ( chars[curPos+1] == 'P' && chars[curPos+2] == 'C' &&
chars[curPos+3] == 'D' && chars[curPos+4] == 'A' &&
chars[curPos+5] == 'T' && chars[curPos+6] == 'A' ) {
curPos += 7;
scanningFunction = ScanningFunction.Element6;
return Token.PCDATA;
}
else {
Throw( curPos + 1, Res.Xml_ExpectPcData );
}
}
scanningFunction = ScanningFunction.Element3;
return Token.None;
}
private Token ScanElement3() {
switch ( chars[curPos] ) {
case '(':
curPos++;
return Token.LeftParen;
case '>':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.GreaterThan;
default:
ScanQName();
scanningFunction = ScanningFunction.Element4;
return Token.QName;
}
}
private Token ScanElement4() {
scanningFunction = ScanningFunction.Element5;
Token t;
switch ( chars[curPos] ) {
case '*':
t = Token.Star;
break;
case '?':
t = Token.QMark;
break;
case '+':
t = Token.Plus;
break;
default:
return Token.None;
}
if ( whitespaceSeen ) {
Throw( curPos, Res.Xml_ExpectNoWhitespace );
}
curPos++;
return t;
}
private Token ScanElement5() {
switch ( chars[curPos] ) {
case ',':
curPos++;
scanningFunction = ScanningFunction.Element3;
return Token.Comma;
case '|':
curPos++;
scanningFunction = ScanningFunction.Element3;
return Token.Or;
case ')':
curPos++;
scanningFunction = ScanningFunction.Element4;
return Token.RightParen;
case '>':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.GreaterThan;
default:
Throw( curPos, Res.Xml_ExpectOp );
return Token.None;
}
}
private Token ScanElement6() {
switch ( chars[curPos] ) {
case ')':
curPos++;
scanningFunction = ScanningFunction.Element7;
return Token.RightParen;
case '|':
curPos++;
nextScaningFunction = ScanningFunction.Element6;
scanningFunction = ScanningFunction.QName;
return Token.Or;
default:
ThrowUnexpectedToken( curPos, ")", "|" );
return Token.None;
}
}
private Token ScanElement7() {
scanningFunction = ScanningFunction.ClosingTag;
if ( chars[curPos] == '*' && !whitespaceSeen ) {
curPos++;
return Token.Star;
}
return Token.None;
}
private Token ScanAttlist1() {
switch ( chars[curPos] ) {
case '>':
curPos++;
scanningFunction = ScanningFunction.SubsetContent;
return Token.GreaterThan;
default:
if ( !whitespaceSeen ) {
Throw( curPos, Res.Xml_ExpectingWhiteSpace, ParseUnexpectedToken( curPos ) );
}
ScanQName();
scanningFunction = ScanningFunction.Attlist2;
return Token.QName;
}
}
private Token ScanAttlist2() {
for (;;) {
switch ( chars[curPos] ) {
case '(':
curPos++;
scanningFunction = ScanningFunction.Nmtoken;
nextScaningFunction = ScanningFunction.Attlist5;
return Token.LeftParen;
case 'C':
if ( charsUsed - curPos < 5 )
goto ReadData;
if ( chars[curPos+1] != 'D' || chars[curPos+2] != 'A' ||
chars[curPos+3] != 'T' || chars[curPos+4] != 'A' ) {
Throw( curPos, Res.Xml_InvalidAttributeType1 );
}
curPos += 5;
scanningFunction = ScanningFunction.Attlist6;
return Token.CDATA;
case 'E':
if ( charsUsed - curPos < 9 )
goto ReadData;
scanningFunction = ScanningFunction.Attlist6;
if ( chars[curPos+1] != 'N' || chars[curPos+2] != 'T' ||
chars[curPos+3] != 'I' || chars[curPos+4] != 'T' ) {
Throw( curPos, Res.Xml_InvalidAttributeType );
}
switch ( chars[curPos+5] ) {
case 'I':
if ( chars[curPos+6] != 'E' || chars[curPos+7] != 'S' ) {
Throw( curPos, Res.Xml_InvalidAttributeType );
}
curPos += 8;
return Token.ENTITIES;
case 'Y':
curPos += 6;
return Token.ENTITY;
default:
Throw( curPos, Res.Xml_InvalidAttributeType );
break;
}
break;
case 'I':
if ( charsUsed - curPos < 6 )
goto ReadData;
scanningFunction = ScanningFunction.Attlist6;
if ( chars[curPos+1] != 'D' ) {
Throw( curPos, Res.Xml_InvalidAttributeType );
}
if ( chars[curPos+2] != 'R' ) {
curPos += 2;
return Token.ID;
}
if ( chars[curPos+3] != 'E' || chars[curPos+4] != 'F' ) {
Throw( curPos, Res.Xml_InvalidAttributeType );
}
if ( chars[curPos+5] != 'S' ) {
curPos += 5;
return Token.IDREF;
}
else {
curPos += 6;
return Token.IDREFS;
}
case 'N':
if ( charsUsed - curPos < 8 && !readerAdapter.IsEof ) {
goto ReadData;
}
switch ( chars[curPos+1] ) {
case 'O':
if ( chars[curPos+2] != 'T' || chars[curPos+3] != 'A' ||
chars[curPos+4] != 'T' || chars[curPos+5] != 'I' ||
chars[curPos+6] != 'O' || chars[curPos+7] != 'N' ) {
Throw( curPos, Res.Xml_InvalidAttributeType );
}
curPos += 8;
scanningFunction = ScanningFunction.Attlist3;
return Token.NOTATION;
case 'M':
if ( chars[curPos+2] != 'T' || chars[curPos+3] != 'O' ||
chars[curPos+4] != 'K' || chars[curPos+5] != 'E' ||
chars[curPos+6] != 'N' ) {
Throw( curPos, Res.Xml_InvalidAttributeType );
}
scanningFunction = ScanningFunction.Attlist6;
if ( chars[curPos+7] == 'S' ) {
curPos += 8;
return Token.NMTOKENS;
}
else {
curPos += 7;
return Token.NMTOKEN;
}
default:
Throw( curPos, Res.Xml_InvalidAttributeType );
break;
}
break;
default:
Throw( curPos, Res.Xml_InvalidAttributeType );
break;
}
ReadData:
if ( ReadData() == 0 ) {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
}
}
private Token ScanAttlist3() {
if ( chars[curPos] == '(' ) {
curPos++;
scanningFunction = ScanningFunction.Name;
nextScaningFunction = ScanningFunction.Attlist4;
return Token.LeftParen;
}
else {
ThrowUnexpectedToken( curPos, "(" );
return Token.None;
}
}
private Token ScanAttlist4() {
switch ( chars[curPos] ) {
case ')':
curPos++;
scanningFunction = ScanningFunction.Attlist6;
return Token.RightParen;
case '|':
curPos++;
scanningFunction = ScanningFunction.Name;
nextScaningFunction = ScanningFunction.Attlist4;
return Token.Or;
default:
ThrowUnexpectedToken( curPos, ")", "|" );
return Token.None;
}
}
private Token ScanAttlist5() {
switch ( chars[curPos] ) {
case ')':
curPos++;
scanningFunction = ScanningFunction.Attlist6;
return Token.RightParen;
case '|':
curPos++;
scanningFunction = ScanningFunction.Nmtoken;
nextScaningFunction = ScanningFunction.Attlist5;
return Token.Or;
default:
ThrowUnexpectedToken( curPos, ")", "|" );
return Token.None;
}
}
private Token ScanAttlist6() {
for (;;) {
switch ( chars[curPos] ) {
case '"':
case '\'':
ScanLiteral( LiteralType.AttributeValue );
scanningFunction = ScanningFunction.Attlist1;
return Token.Literal;
case '#':
if ( charsUsed - curPos < 6 )
goto ReadData;
switch ( chars[curPos+1] ) {
case 'R':
if ( charsUsed - curPos < 9 )
goto ReadData;
if ( chars[curPos+2] != 'E' || chars[curPos+3] != 'Q' ||
chars[curPos+4] != 'U' || chars[curPos+5] != 'I' ||
chars[curPos+6] != 'R' || chars[curPos+7] != 'E' ||
chars[curPos+8] != 'D' ) {
Throw( curPos, Res.Xml_ExpectAttType );
}
curPos += 9;
scanningFunction = ScanningFunction.Attlist1;
return Token.REQUIRED;
case 'I':
if ( charsUsed - curPos < 8 )
goto ReadData;
if ( chars[curPos+2] != 'M' || chars[curPos+3] != 'P' ||
chars[curPos+4] != 'L' || chars[curPos+5] != 'I' ||
chars[curPos+6] != 'E' || chars[curPos+7] != 'D' ) {
Throw( curPos, Res.Xml_ExpectAttType );
}
curPos += 8;
scanningFunction = ScanningFunction.Attlist1;
return Token.IMPLIED;
case 'F':
if ( chars[curPos+2] != 'I' || chars[curPos+3] != 'X' ||
chars[curPos+4] != 'E' || chars[curPos+5] != 'D' ) {
Throw( curPos, Res.Xml_ExpectAttType );
}
curPos += 6;
scanningFunction = ScanningFunction.Attlist7;
return Token.FIXED;
default:
Throw( curPos, Res.Xml_ExpectAttType );
break;
}
break;
default:
Throw( curPos, Res.Xml_ExpectAttType );
break;
}
ReadData:
if ( ReadData() == 0 ) {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
}
}
private Token ScanAttlist7() {
switch ( chars[curPos] ) {
case '"':
case '\'':
ScanLiteral( LiteralType.AttributeValue );
scanningFunction = ScanningFunction.Attlist1;
return Token.Literal;
default:
ThrowUnexpectedToken( curPos, "\"", "'" );
return Token.None;
}
}
private Token ScanLiteral( LiteralType literalType ) {
Debug.Assert( chars[curPos] == '"' || chars[curPos] == '\'' );
char quoteChar = chars[curPos];
char replaceChar = ( literalType == LiteralType.AttributeValue ) ? (char)0x20 : (char)0xA;
int startQuoteEntityId = currentEntityId;
literalLineInfo.Set( LineNo, LinePos );
curPos++;
tokenStartPos = curPos;
#if SILVERLIGHT
stringBuilder.Clear();
#else
stringBuilder.Length = 0;
#endif
for (;;) {
#if SILVERLIGHT
while ( xmlCharType.IsAttributeValueChar( chars[curPos] ) && chars[curPos] != '%' ) {
curPos++;
}
#else
unsafe {
while ( ( xmlCharType.charProperties[chars[curPos]] & XmlCharType.fAttrValue ) != 0 && chars[curPos] != '%' ) {
curPos++;
}
}
#endif
if ( chars[curPos] == quoteChar && currentEntityId == startQuoteEntityId ) {
if ( stringBuilder.Length > 0 ) {
stringBuilder.Append( chars, tokenStartPos, curPos - tokenStartPos );
}
curPos++;
literalQuoteChar = quoteChar;
return Token.Literal;
}
int tmp1 = curPos - tokenStartPos;
if ( tmp1 > 0 ) {
stringBuilder.Append( chars, tokenStartPos, tmp1 );
tokenStartPos = curPos;
}
switch ( chars[curPos] ) {
case '"':
case '\'':
case '>':
curPos++;
continue;
// eol
case (char)0xA:
curPos++;
if ( Normalize ) {
stringBuilder.Append( replaceChar ); // For attributes: CDATA normalization of 0xA
tokenStartPos = curPos;
}
readerAdapter.OnNewLine( curPos );
continue;
case (char)0xD:
if ( chars[curPos+1] == (char)0xA ) {
if ( Normalize ) {
if ( literalType == LiteralType.AttributeValue ) {
stringBuilder.Append( readerAdapter.IsEntityEolNormalized ? "\u0020\u0020" : "\u0020" ); // CDATA normalization of 0xD 0xA
}
else {
stringBuilder.Append( readerAdapter.IsEntityEolNormalized ? "\u000D\u000A" : "\u000A" ); // EOL normalization of 0xD 0xA
}
tokenStartPos = curPos + 2;
SaveParsingBuffer(); // EOL normalization of 0xD 0xA in internal subset value
readerAdapter.CurrentPosition++;
}
curPos += 2;
}
else if ( curPos+1 == charsUsed ) {
goto ReadData;
}
else {
curPos++;
if ( Normalize ) {
stringBuilder.Append( replaceChar ); // For attributes: CDATA normalization of 0xD and 0xD 0xA
tokenStartPos = curPos; // For entities: EOL normalization of 0xD and 0xD 0xA
}
}
readerAdapter.OnNewLine( curPos );
continue;
// tab
case (char)0x9:
if ( literalType == LiteralType.AttributeValue && Normalize ) {
stringBuilder.Append( (char)0x20 ); // For attributes: CDATA normalization of 0x9
tokenStartPos++;
}
curPos++;
continue;
// attribute values cannot contain '<'
case '<':
if ( literalType == LiteralType.AttributeValue ) {
Throw( curPos, Res.Xml_BadAttributeChar, XmlException.BuildCharExceptionArgs( '<', '\0' ) );
}
curPos++;
continue;
// parameter entity reference
case '%':
if ( literalType != LiteralType.EntityReplText ) {
curPos++;
continue;
}
HandleEntityReference( true, true, literalType == LiteralType.AttributeValue );
tokenStartPos = curPos;
continue;
// general entity reference
case '&':
if ( literalType == LiteralType.SystemOrPublicID ) {
curPos++;
continue;
}
if ( curPos + 1 == charsUsed ) {
goto ReadData;
}
// numeric characters reference
if ( chars[curPos + 1] == '#' ) {
SaveParsingBuffer();
int endPos = readerAdapter.ParseNumericCharRef( SaveInternalSubsetValue ? internalSubsetValueSb : null );
LoadParsingBuffer();
stringBuilder.Append( chars, curPos, endPos - curPos );
readerAdapter.CurrentPosition = endPos;
tokenStartPos = endPos;
curPos = endPos;
continue;
}
else {
// general entity reference
SaveParsingBuffer();
if ( literalType == LiteralType.AttributeValue ) {
int endPos = readerAdapter.ParseNamedCharRef( true, SaveInternalSubsetValue ? internalSubsetValueSb : null );
LoadParsingBuffer();
if ( endPos >= 0 ) {
stringBuilder.Append( chars, curPos, endPos - curPos );
readerAdapter.CurrentPosition = endPos;
tokenStartPos = endPos;
curPos = endPos;
continue;
}
else {
HandleEntityReference( false, true, true );
tokenStartPos = curPos;
}
continue;
}
else {
int endPos = readerAdapter.ParseNamedCharRef( false, null );
LoadParsingBuffer();
if ( endPos >= 0 ) {
tokenStartPos = curPos;
curPos = endPos;
continue;
}
else {
stringBuilder.Append( '&' );
curPos++;
tokenStartPos = curPos;
// Bypass general entities in entity values
XmlQualifiedName entityName = ScanEntityName();
VerifyEntityReference( entityName, false, false, false );
}
continue;
}
}
default:
// end of buffer
if ( curPos == charsUsed ) {
goto ReadData;
}
// surrogate chars
else {
char ch = chars[curPos];
if ( XmlCharType.IsHighSurrogate(ch) ) {
if ( curPos + 1 == charsUsed ) {
goto ReadData;
}
curPos++;
if ( XmlCharType.IsLowSurrogate(chars[curPos]) ) {
curPos++;
continue;
}
}
ThrowInvalidChar( chars, charsUsed, curPos );
return Token.None;
}
}
ReadData:
Debug.Assert( curPos - tokenStartPos == 0 );
// read new characters into the buffer
if ( readerAdapter.IsEof || ReadData() == 0 ) {
if ( literalType == LiteralType.SystemOrPublicID || !HandleEntityEnd( true ) ) {
Throw( curPos, Res.Xml_UnclosedQuote );
}
}
tokenStartPos = curPos;
}
}
private XmlQualifiedName ScanEntityName() {
try {
ScanName();
}
catch ( XmlException e ) {
Throw( Res.Xml_ErrorParsingEntityName, string.Empty, e.LineNumber, e.LinePosition );
}
if ( chars[curPos] != ';' ) {
ThrowUnexpectedToken( curPos, ";" );
}
XmlQualifiedName entityName = GetNameQualified( false );
curPos++;
return entityName;
}
private Token ScanNotation1() {
switch ( chars[curPos] ) {
case 'P':
if ( !EatPublicKeyword() ) {
Throw( curPos, Res.Xml_ExpectExternalOrClose );
}
nextScaningFunction = ScanningFunction.ClosingTag;
scanningFunction = ScanningFunction.PublicId1;
return Token.PUBLIC;
case 'S':
if ( !EatSystemKeyword() ) {
Throw( curPos, Res.Xml_ExpectExternalOrClose );
}
nextScaningFunction = ScanningFunction.ClosingTag;
scanningFunction = ScanningFunction.SystemId;
return Token.SYSTEM;
default:
Throw( curPos, Res.Xml_ExpectExternalOrPublicId );
return Token.None;
}
}
private Token ScanSystemId() {
if ( chars[curPos] != '"' && chars[curPos] != '\'' ) {
ThrowUnexpectedToken( curPos, "\"", "'" );
}
ScanLiteral( LiteralType.SystemOrPublicID );
scanningFunction = nextScaningFunction;
return Token.Literal;
}
private Token ScanEntity1() {
if ( chars[curPos] == '%' ) {
curPos++;
nextScaningFunction = ScanningFunction.Entity2;
scanningFunction = ScanningFunction.Name;
return Token.Percent;
}
else {
ScanName();
scanningFunction = ScanningFunction.Entity2;
return Token.Name;
}
}
private Token ScanEntity2() {
switch ( chars[curPos] ) {
case 'P':
if ( !EatPublicKeyword() ) {
Throw( curPos, Res.Xml_ExpectExternalOrClose );
}
nextScaningFunction = ScanningFunction.Entity3;
scanningFunction = ScanningFunction.PublicId1;
return Token.PUBLIC;
case 'S':
if ( !EatSystemKeyword() ) {
Throw( curPos, Res.Xml_ExpectExternalOrClose );
}
nextScaningFunction = ScanningFunction.Entity3;
scanningFunction = ScanningFunction.SystemId;
return Token.SYSTEM;
case '"':
case '\'':
ScanLiteral( LiteralType.EntityReplText );
scanningFunction = ScanningFunction.ClosingTag;
return Token.Literal;
default:
Throw( curPos, Res.Xml_ExpectExternalIdOrEntityValue );
return Token.None;
}
}
private Token ScanEntity3() {
if ( chars[curPos] == 'N' ) {
while ( charsUsed - curPos < 5 ) {
if ( ReadData() == 0 ) {
goto End;
}
}
if ( chars[curPos+1] == 'D' && chars[curPos+2] == 'A' &&
chars[curPos+3] == 'T' && chars[curPos+4] == 'A' ) {
curPos += 5;
scanningFunction = ScanningFunction.Name;
nextScaningFunction = ScanningFunction.ClosingTag;
return Token.NData;
}
}
End:
scanningFunction = ScanningFunction.ClosingTag;
return Token.None;
}
private Token ScanPublicId1() {
if ( chars[curPos] != '"' && chars[curPos] != '\'' ) {
ThrowUnexpectedToken( curPos, "\"", "'" );
}
ScanLiteral( LiteralType.SystemOrPublicID );
scanningFunction = ScanningFunction.PublicId2;
return Token.Literal;
}
private Token ScanPublicId2() {
if ( chars[curPos] != '"' && chars[curPos] != '\'' ) {
scanningFunction = nextScaningFunction;
return Token.None;
}
ScanLiteral( LiteralType.SystemOrPublicID );
scanningFunction = nextScaningFunction;
return Token.Literal;
}
private Token ScanCondSection1() {
if ( chars[curPos] != 'I' ) {
Throw( curPos, Res.Xml_ExpectIgnoreOrInclude );
}
curPos++;
for (;;) {
if ( charsUsed - curPos < 5 ) {
goto ReadData;
}
switch ( chars[curPos] ) {
case 'N':
if ( charsUsed - curPos < 6 ) {
goto ReadData;
}
if ( chars[curPos+1] != 'C' || chars[curPos+2] != 'L' ||
chars[curPos+3] != 'U' || chars[curPos+4] != 'D' ||
chars[curPos+5] != 'E' || xmlCharType.IsNameSingleChar( chars[curPos+6] )
#if XML10_FIFTH_EDITION
|| xmlCharType.IsNCNameHighSurrogateChar( chars[curPos+6] )
#endif
) {
goto default;
}
nextScaningFunction = ScanningFunction.SubsetContent;
scanningFunction = ScanningFunction.CondSection2;
curPos += 6;
return Token.INCLUDE;
case 'G':
if ( chars[curPos+1] != 'N' || chars[curPos+2] != 'O' ||
chars[curPos+3] != 'R' || chars[curPos+4] != 'E' ||
xmlCharType.IsNameSingleChar( chars[curPos + 5] )
#if XML10_FIFTH_EDITION
||xmlCharType.IsNCNameHighSurrogateChar( chars[curPos+5] )
#endif
) {
goto default;
}
nextScaningFunction = ScanningFunction.CondSection3;
scanningFunction = ScanningFunction.CondSection2;
curPos += 5;
return Token.IGNORE;
default:
Throw( curPos - 1, Res.Xml_ExpectIgnoreOrInclude );
return Token.None;
}
ReadData:
if ( ReadData() == 0 ) {
Throw( curPos, Res.Xml_IncompleteDtdContent );
}
}
}
private Token ScanCondSection2() {
if ( chars[curPos] != '[' ) {
ThrowUnexpectedToken( curPos, "[" );
}
curPos++;
scanningFunction = nextScaningFunction;
return Token.LeftBracket;
}
private Token ScanCondSection3() {
int ignoreSectionDepth = 0;
// skip ignored part
for (;;) {
#if SILVERLIGHT
while ( xmlCharType.IsTextChar(chars[curPos]) && chars[curPos] != ']' ) {
curPos++;
}
#else
unsafe {
while ( ( xmlCharType.charProperties[chars[curPos]] & XmlCharType.fText ) != 0 && chars[curPos] != ']' ) {
curPos++;
}
}
#endif
switch ( chars[curPos] ) {
case '"':
case '\'':
case (char)0x9:
case '&':
curPos++;
continue;
// eol
case (char)0xA:
curPos++;
readerAdapter.OnNewLine( curPos );
continue;
case (char)0xD:
if ( chars[curPos+1] == (char)0xA ) {
curPos += 2;
}
else if ( curPos+1 < charsUsed || readerAdapter.IsEof ) {
curPos++;
}
else {
goto ReadData;
}
readerAdapter.OnNewLine( curPos );
continue;
case '<':
if ( charsUsed - curPos < 3 ) {
goto ReadData;
}
if ( chars[curPos+1] != '!' || chars[curPos+2] != '[' ) {
curPos++;
continue;
}
ignoreSectionDepth++;
curPos += 3;
continue;
case ']':
if ( charsUsed - curPos < 3 ) {
goto ReadData;
}
if ( chars[curPos+1] != ']' || chars[curPos+2] != '>' ) {
curPos++;
continue;
}
if ( ignoreSectionDepth > 0 ) {
ignoreSectionDepth--;
curPos += 3;
continue;
}
else {
curPos += 3;
scanningFunction = ScanningFunction.SubsetContent;
return Token.CondSectionEnd;
}
default:
// end of buffer
if ( curPos == charsUsed ) {
goto ReadData;
}
// surrogate chars
else {
char ch = chars[curPos];
if ( XmlCharType.IsHighSurrogate(ch) ) {
if ( curPos + 1 == charsUsed ) {
goto ReadData;
}
curPos++;
if ( XmlCharType.IsLowSurrogate(chars[curPos])) {
curPos++;
continue;
}
}
ThrowInvalidChar( chars, charsUsed, curPos );
return Token.None;
}
}
ReadData:
// read new characters into the buffer
if ( readerAdapter.IsEof || ReadData() == 0 ) {
if ( HandleEntityEnd( false ) ) {
continue;
}
Throw( curPos, Res.Xml_UnclosedConditionalSection );
}
tokenStartPos = curPos;
}
}
private void ScanName() {
ScanQName( false );
}
private void ScanQName() {
ScanQName( SupportNamespaces );
}
private void ScanQName( bool isQName ) {
tokenStartPos = curPos;
int colonOffset = -1;
for (;;) {
unsafe {
#if SILVERLIGHT
if ( xmlCharType.IsStartNCNameSingleChar(chars[curPos]) || chars[curPos] == ':' ) {
#else
if ( ( xmlCharType.charProperties[chars[curPos]] & XmlCharType.fNCStartNameSC ) != 0 || chars[curPos] == ':') { // if ( xmlCharType.IsStartNCNameSingleChar(chars[curPos]) || chars[curPos] == ':' ) {
#endif
curPos++;
}
#if XML10_FIFTH_EDITION
else if ( curPos + 1 < charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[curPos+1], chars[curPos])) {
curPos += 2;
}
#endif
else {
if ( curPos + 1 >= charsUsed ) {
if ( ReadDataInName() ) {
continue;
}
Throw( curPos, Res.Xml_UnexpectedEOF, "Name" );
}
else {
Throw( curPos, Res.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs( chars, charsUsed, curPos ) );
}
}
}
ContinueName:
unsafe {
for (;;) {
#if SILVERLIGHT
if ( xmlCharType.IsNCNameSingleChar( chars[curPos] ) ) {
#else
if ( ( xmlCharType.charProperties[chars[curPos]] & XmlCharType.fNCNameSC ) != 0 ) { // while ( xmlCharType.IsNCNameSingleChar(chars[curPos]) ) {
#endif
curPos++;
}
#if XML10_FIFTH_EDITION
else if ( curPos + 1 < charsUsed && xmlCharType.IsNameSurrogateChar(chars[curPos + 1], chars[curPos]) ) {
curPos += 2;
}
#endif
else {
break;
}
}
}
if ( chars[curPos] == ':' ) {
if ( isQName ) {
if ( colonOffset != -1 ) {
Throw( curPos, Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs( ':', '\0' ));
}
colonOffset = curPos - tokenStartPos;
curPos++;
continue;
}
else {
curPos++;
goto ContinueName;
}
}
// end of buffer
else if ( curPos == charsUsed
#if XML10_FIFTH_EDITION
|| ( curPos + 1 == charsUsed && xmlCharType.IsNCNameHighSurrogateChar( chars[curPos] ) )
#endif
) {
if ( ReadDataInName() ) {
goto ContinueName;
}
if ( tokenStartPos == curPos ) {
Throw( curPos, Res.Xml_UnexpectedEOF, "Name" );
}
}
// end of name
colonPos = ( colonOffset == -1 ) ? -1 : tokenStartPos + colonOffset;
return;
}
}
private bool ReadDataInName() {
int offset = curPos - tokenStartPos;
curPos = tokenStartPos;
bool newDataRead = ( ReadData() != 0 );
tokenStartPos = curPos;
curPos += offset;
return newDataRead;
}
private void ScanNmtoken() {
tokenStartPos = curPos;
for (;;) {
unsafe {
for (;;) {
#if SILVERLIGHT
if ( xmlCharType.IsNCNameSingleChar(chars[curPos]) || chars[curPos] == ':' ) {
#else
if ((xmlCharType.charProperties[chars[curPos]] & XmlCharType.fNCNameSC) != 0 || chars[curPos] == ':') { // if ( xmlCharType.IsNCNameChar(chars[curPos]) || chars[curPos] == ':' ) {
#endif
curPos++;
}
#if XML10_FIFTH_EDITION
else if (curPos + 1 < charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[curPos + 1], chars[curPos])) {
curPos += 2;
}
#endif
else {
break;
}
}
}
if ( curPos < charsUsed
#if XML10_FIFTH_EDITION
&& ( !xmlCharType.IsNCNameHighSurrogateChar( chars[curPos] ) || curPos + 1 < charsUsed )
#endif
) {
if ( curPos - tokenStartPos == 0 ) {
Throw( curPos, Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs( chars, charsUsed, curPos ) );
}
return;
}
int len = curPos - tokenStartPos;
curPos = tokenStartPos;
if ( ReadData() == 0 ) {
if ( len > 0 ) {
tokenStartPos = curPos;
curPos += len;
return;
}
Throw( curPos, Res.Xml_UnexpectedEOF, "NmToken" );
}
tokenStartPos = curPos;
curPos += len;
}
}
private bool EatPublicKeyword() {
Debug.Assert( chars[curPos] == 'P' );
while ( charsUsed - curPos < 6 ) {
if ( ReadData() == 0 ) {
return false;
}
}
if ( chars[curPos+1] != 'U' || chars[curPos+2] != 'B' ||
chars[curPos+3] != 'L' || chars[curPos+4] != 'I' ||
chars[curPos+5] != 'C' ) {
return false;
}
curPos += 6;
return true;
}
private bool EatSystemKeyword() {
Debug.Assert( chars[curPos] == 'S' );
while ( charsUsed - curPos < 6 ) {
if ( ReadData() == 0 ) {
return false;
}
}
if ( chars[curPos+1] != 'Y' || chars[curPos+2] != 'S' ||
chars[curPos+3] != 'T' || chars[curPos+4] != 'E' ||
chars[curPos+5] != 'M' ) {
return false;
}
curPos += 6;
return true;
}
//
// Scanned data getters
//
private XmlQualifiedName GetNameQualified( bool canHavePrefix ) {
Debug.Assert( curPos - tokenStartPos > 0 );
if ( colonPos == -1 ) {
return new XmlQualifiedName( nameTable.Add( chars, tokenStartPos, curPos - tokenStartPos ) );
}
else {
if ( canHavePrefix ) {
return new XmlQualifiedName( nameTable.Add( chars, colonPos + 1, curPos - colonPos - 1 ),
nameTable.Add( chars, tokenStartPos, colonPos - tokenStartPos ) );
}
else {
Throw( tokenStartPos, Res.Xml_ColonInLocalName, GetNameString() );
return null;
}
}
}
private string GetNameString() {
Debug.Assert( curPos - tokenStartPos > 0 );
return new string( chars, tokenStartPos, curPos - tokenStartPos );
}
private string GetNmtokenString() {
return GetNameString();
}
private string GetValue() {
if ( stringBuilder.Length == 0 ) {
return new string( chars, tokenStartPos, curPos - tokenStartPos - 1 );
}
else {
return stringBuilder.ToString();
}
}
private string GetValueWithStrippedSpaces() {
Debug.Assert( curPos == 0 || chars[curPos-1] == '"' || chars[curPos-1] == '\'' );
// We cannot StripSpaces directly in the buffer - we need the original value inthe buffer intact so that the internal subset value is correct
string val = ( stringBuilder.Length == 0 ) ? new string( chars, tokenStartPos, curPos - tokenStartPos - 1 ) : stringBuilder.ToString();
return StripSpaces( val );
}
//
// Parsing buffer maintainance methods
//
int ReadData() {
SaveParsingBuffer();
int charsRead = readerAdapter.ReadData();
LoadParsingBuffer();
return charsRead;
}
private void LoadParsingBuffer() {
chars = readerAdapter.ParsingBuffer;
charsUsed = readerAdapter.ParsingBufferLength;
curPos = readerAdapter.CurrentPosition;
}
private void SaveParsingBuffer() {
SaveParsingBuffer( curPos );
}
private void SaveParsingBuffer( int internalSubsetValueEndPos ) {
if ( SaveInternalSubsetValue ) {
Debug.Assert( internalSubsetValueSb != null );
int readerCurPos = readerAdapter.CurrentPosition;
if ( internalSubsetValueEndPos - readerCurPos > 0 ) {
internalSubsetValueSb.Append( chars, readerCurPos, internalSubsetValueEndPos - readerCurPos );
}
}
readerAdapter.CurrentPosition = curPos;
}
//
// Entity handling
//
private bool HandleEntityReference( bool paramEntity, bool inLiteral, bool inAttribute ) {
Debug.Assert( chars[curPos] == '&' || chars[curPos] == '%' );
curPos++;
return HandleEntityReference( ScanEntityName(), paramEntity, inLiteral, inAttribute );
}
private bool HandleEntityReference( XmlQualifiedName entityName, bool paramEntity, bool inLiteral, bool inAttribute ) {
Debug.Assert( chars[curPos-1] == ';' );
SaveParsingBuffer();
if ( paramEntity && ParsingInternalSubset && !ParsingTopLevelMarkup ) {
Throw( curPos - entityName.Name.Length - 1, Res.Xml_InvalidParEntityRef );
}
SchemaEntity entity = VerifyEntityReference( entityName, paramEntity, true, inAttribute );
if ( entity == null ) {
return false;
}
if ( entity.ParsingInProgress ) {
Throw( curPos - entityName.Name.Length - 1, paramEntity ? Res.Xml_RecursiveParEntity : Res.Xml_RecursiveGenEntity, entityName.Name );
}
int newEntityId;
if ( entity.IsExternal ) {
if ( !readerAdapter.PushEntity( entity, out newEntityId ) ) {
return false;
}
externalEntitiesDepth++;
}
else {
if ( entity.Text.Length == 0 ) {
return false;
}
if ( !readerAdapter.PushEntity( entity, out newEntityId ) ) {
return false;
}
}
currentEntityId = newEntityId;
if ( paramEntity && !inLiteral && scanningFunction != ScanningFunction.ParamEntitySpace ) {
savedScanningFunction = scanningFunction;
scanningFunction = ScanningFunction.ParamEntitySpace;
}
LoadParsingBuffer();
return true;
}
private bool HandleEntityEnd( bool inLiteral ) {
SaveParsingBuffer();
IDtdEntityInfo oldEntity;
if ( !readerAdapter.PopEntity( out oldEntity, out currentEntityId ) ) {
return false;
}
LoadParsingBuffer();
if ( oldEntity == null ) {
// external subset popped
Debug.Assert( !ParsingInternalSubset || freeFloatingDtd );
Debug.Assert( currentEntityId == 0 );
if ( scanningFunction == ScanningFunction.ParamEntitySpace ) {
scanningFunction = savedScanningFunction;
}
return false;
}
if ( oldEntity.IsExternal ) {
externalEntitiesDepth--;
}
if ( !inLiteral && scanningFunction != ScanningFunction.ParamEntitySpace ) {
savedScanningFunction = scanningFunction;
scanningFunction = ScanningFunction.ParamEntitySpace;
}
return true;
}
private SchemaEntity VerifyEntityReference( XmlQualifiedName entityName, bool paramEntity, bool mustBeDeclared, bool inAttribute ) {
Debug.Assert( chars[curPos-1] == ';' );
SchemaEntity entity;
if ( paramEntity ) {
schemaInfo.ParameterEntities.TryGetValue(entityName, out entity);
}
else {
schemaInfo.GeneralEntities.TryGetValue(entityName, out entity);
}
if ( entity == null ) {
if ( paramEntity ) {
#if !SILVERLIGHT
if ( validate ) {
SendValidationEvent( curPos - entityName.Name.Length - 1, XmlSeverityType.Error, Res.Xml_UndeclaredParEntity, entityName.Name );
}
#endif
}
else if ( mustBeDeclared ) {
if ( !ParsingInternalSubset ) {
#if !SILVERLIGHT
if (validate) {
SendValidationEvent( curPos - entityName.Name.Length - 1, XmlSeverityType.Error, Res.Xml_UndeclaredEntity, entityName.Name );
}
#endif
}
else {
Throw( curPos - entityName.Name.Length - 1, Res.Xml_UndeclaredEntity, entityName.Name );
}
}
return null;
}
if ( !entity.NData.IsEmpty ) {
Throw( curPos - entityName.Name.Length - 1, Res.Xml_UnparsedEntityRef, entityName.Name );
}
if ( inAttribute && entity.IsExternal ) {
Throw( curPos - entityName.Name.Length - 1, Res.Xml_ExternalEntityInAttValue, entityName.Name );
}
return entity;
}
//
// Helper methods and properties
//
#if !SILVERLIGHT
private void SendValidationEvent( int pos, XmlSeverityType severity, string code, string arg ) {
Debug.Assert( validate );
SendValidationEvent( severity, new XmlSchemaException( code, arg, BaseUriStr, (int)LineNo, (int)LinePos + ( pos - curPos ) ) );
}
private void SendValidationEvent( XmlSeverityType severity, string code, string arg ) {
Debug.Assert( validate );
SendValidationEvent( severity, new XmlSchemaException( code, arg, BaseUriStr, (int)LineNo, (int)LinePos ) );
}
private void SendValidationEvent( XmlSeverityType severity, XmlSchemaException e ) {
Debug.Assert( validate );
IValidationEventHandling eventHandling = readerAdapterWithValidation.ValidationEventHandling;
if ( eventHandling != null ) {
eventHandling.SendEvent(e, severity);
}
}
#endif
private bool IsAttributeValueType( Token token ) {
return (int)token >= (int)Token.CDATA && (int)token <= (int)Token.NOTATION;
}
private int LineNo {
get {
return readerAdapter.LineNo;
}
}
private int LinePos {
get {
return curPos - readerAdapter.LineStartPosition;
}
}
private string BaseUriStr {
get {
Uri tmp = readerAdapter.BaseUri;
return ( tmp != null ) ? tmp.ToString() : string.Empty;
}
}
private void OnUnexpectedError() {
Debug.Assert( false, "This is an unexpected error that should have been handled in the ScanXXX methods." );
Throw( curPos, Res.Xml_InternalError );
}
void Throw( int curPos, string res ) {
Throw( curPos, res, string.Empty );
}
void Throw( int curPos, string res, string arg ) {
this.curPos = curPos;
Uri baseUri = readerAdapter.BaseUri;
readerAdapter.Throw( new XmlException( res, arg, (int)LineNo, (int)LinePos, baseUri == null ? null : baseUri.ToString() ) );
}
void Throw( int curPos, string res, string[] args ) {
this.curPos = curPos;
Uri baseUri = readerAdapter.BaseUri;
readerAdapter.Throw( new XmlException( res, args, (int)LineNo, (int)LinePos, baseUri == null ? null : baseUri.ToString() ) );
}
void Throw( string res, string arg, int lineNo, int linePos ) {
Uri baseUri = readerAdapter.BaseUri;
readerAdapter.Throw( new XmlException( res, arg, (int)lineNo, (int)linePos, baseUri == null ? null : baseUri.ToString() ) );
}
void ThrowInvalidChar( int pos, string data, int invCharPos ) {
Throw( pos, Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs( data, invCharPos ));
}
void ThrowInvalidChar( char[] data, int length, int invCharPos ) {
Throw( invCharPos, Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs( data, length, invCharPos ) );
}
private void ThrowUnexpectedToken( int pos, string expectedToken ) {
ThrowUnexpectedToken( pos, expectedToken, null );
}
private void ThrowUnexpectedToken( int pos, string expectedToken1, string expectedToken2 ) {
string unexpectedToken = ParseUnexpectedToken( pos );
if ( expectedToken2 != null ) {
Throw( curPos, Res.Xml_UnexpectedTokens2, new string[3] { unexpectedToken, expectedToken1, expectedToken2 } );
}
else {
Throw( curPos, Res.Xml_UnexpectedTokenEx, new string[2] { unexpectedToken, expectedToken1 } );
}
}
private string ParseUnexpectedToken( int startPos ) {
if ( xmlCharType.IsNCNameSingleChar( chars[startPos] )
#if XML10_FIFTH_EDITION
|| xmlCharType.IsNCNameHighSurrogateChar( chars[startPos] )
#endif
) { // postpone the proper surrogate checking to the loop below
int endPos = startPos;
for (;;) {
if ( xmlCharType.IsNCNameSingleChar( chars[endPos] ) ) {
endPos++;
}
#if XML10_FIFTH_EDITION
else if ( chars[endPos] != 0 && // check for end of the buffer
xmlCharType.IsNCNameSurrogateChar( chars[endPos], chars[endPos + 1] ) ) {
endPos += 2;
}
#endif
else {
break;
}
}
int len = endPos - startPos;
return new string( chars, startPos, len > 0 ? len : 1 );
}
else {
Debug.Assert( startPos < charsUsed );
return new string( chars, startPos, 1 );
}
}
// StripSpaces removes spaces at the beginning and at the end of the value and replaces sequences of spaces with a single space
// !!! This method exists in 2 copies, here and in the XmlTextReaderImplHelper.cs
// If you're changing this one, please also fix the other one.
// (This is necessary due to packaging)
internal static string StripSpaces(string value) {
int len = value.Length;
if (len <= 0) {
return string.Empty;
}
int startPos = 0;
StringBuilder norValue = null;
while (value[startPos] == 0x20) {
startPos++;
if (startPos == len) {
return " ";
}
}
int i;
for (i = startPos; i < len; i++) {
if (value[i] == 0x20) {
int j = i + 1;
while (j < len && value[j] == 0x20) {
j++;
}
if (j == len) {
if (norValue == null) {
return value.Substring(startPos, i - startPos);
}
else {
norValue.Append(value, startPos, i - startPos);
return norValue.ToString();
}
}
if (j > i + 1) {
if (norValue == null) {
norValue = new StringBuilder(len);
}
norValue.Append(value, startPos, i - startPos + 1);
startPos = j;
i = j - 1;
}
}
}
if (norValue == null) {
return (startPos == 0) ? value : value.Substring(startPos, len - startPos);
}
else {
if (i > startPos) {
norValue.Append(value, startPos, i - startPos);
}
return norValue.ToString();
}
}
}
}
|