2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="ContentValidator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// <owner current="true" primary="true">Microsoft</owner>
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Text ;
using System.Diagnostics ;
namespace System.Xml.Schema {
#region ExceptionSymbolsPositions
/// <summary>
/// UPA violations will throw this exception
/// </summary>
class UpaException : Exception {
object particle1 ;
object particle2 ;
public UpaException ( object particle1 , object particle2 ) {
this . particle1 = particle1 ;
this . particle2 = particle2 ;
}
public object Particle1 { get { return particle1 ; } }
public object Particle2 { get { return particle2 ; } }
}
/// <summary>
/// SymbolsDictionary is a map between names that ContextValidator recognizes and symbols - int symbol[XmlQualifiedName name].
/// There are two types of name - full names and wildcards (namespace is specified, local name is anythig).
/// Wildcard excludes all full names that would match by the namespace part.
/// SymbolsDictionry alwas recognizes all the symbols - the last one is a true wildcard -
/// both name and namespace can be anything that none of the other symbols matched.
/// </summary>
class SymbolsDictionary {
int last = 0 ;
Hashtable names ;
Hashtable wildcards = null ;
ArrayList particles ;
object particleLast = null ;
bool isUpaEnforced = true ;
public SymbolsDictionary ( ) {
names = new Hashtable ( ) ;
particles = new ArrayList ( ) ;
}
public int Count {
// last one is a "*:*" any wildcard
get { return last + 1 ; }
}
public int CountOfNames {
get { return names . Count ; }
}
/// <summary>
/// True is particle can be deterministically attributed from the symbol and conversion to DFA is possible.
/// </summary>
public bool IsUpaEnforced {
get { return isUpaEnforced ; }
set { isUpaEnforced = value ; }
}
/// <summary>
/// Add name and return it's number
/// </summary>
public int AddName ( XmlQualifiedName name , object particle ) {
object lookup = names [ name ] ;
if ( lookup ! = null ) {
int symbol = ( int ) lookup ;
if ( particles [ symbol ] ! = particle ) {
isUpaEnforced = false ;
}
return symbol ;
}
else {
names . Add ( name , last ) ;
particles . Add ( particle ) ;
Debug . Assert ( particles . Count = = last + 1 ) ;
return last + + ;
}
}
public void AddNamespaceList ( NamespaceList list , object particle , bool allowLocal ) {
switch ( list . Type ) {
case NamespaceList . ListType . Any :
particleLast = particle ;
break ;
case NamespaceList . ListType . Other :
// Create a symbol for the excluded namespace, but don't set a particle for it.
AddWildcard ( list . Excluded , null ) ;
if ( ! allowLocal ) {
AddWildcard ( string . Empty , null ) ; //##local is not allowed
}
break ;
case NamespaceList . ListType . Set :
foreach ( string wildcard in list . Enumerate ) {
AddWildcard ( wildcard , particle ) ;
}
break ;
}
}
private void AddWildcard ( string wildcard , object particle ) {
if ( wildcards = = null ) {
wildcards = new Hashtable ( ) ;
}
object lookup = wildcards [ wildcard ] ;
if ( lookup = = null ) {
wildcards . Add ( wildcard , last ) ;
particles . Add ( particle ) ;
Debug . Assert ( particles . Count = = last + 1 ) ;
last + + ;
}
else if ( particle ! = null ) {
particles [ ( int ) lookup ] = particle ;
}
}
public ICollection GetNamespaceListSymbols ( NamespaceList list ) {
ArrayList match = new ArrayList ( ) ;
foreach ( XmlQualifiedName name in names . Keys ) {
if ( name ! = XmlQualifiedName . Empty & & list . Allows ( name ) ) {
match . Add ( names [ name ] ) ;
}
}
if ( wildcards ! = null ) {
foreach ( string wildcard in wildcards . Keys ) {
if ( list . Allows ( wildcard ) ) {
match . Add ( wildcards [ wildcard ] ) ;
}
}
}
if ( list . Type = = NamespaceList . ListType . Any | | list . Type = = NamespaceList . ListType . Other ) {
match . Add ( last ) ; // add wildcard
}
return match ;
}
/// <summary>
/// Find the symbol for the given name. If neither names nor wilcards match it last (*.*) symbol will be returned
/// </summary>
public int this [ XmlQualifiedName name ] {
get {
object lookup = names [ name ] ;
if ( lookup ! = null ) {
return ( int ) lookup ;
}
if ( wildcards ! = null ) {
lookup = wildcards [ name . Namespace ] ;
if ( lookup ! = null ) {
return ( int ) lookup ;
}
}
return last ; // true wildcard
}
}
/// <summary>
/// Check if a name exists in the symbol dictionary
/// </summary>
public bool Exists ( XmlQualifiedName name ) {
object lookup = names [ name ] ;
if ( lookup ! = null ) {
return true ;
}
return false ;
}
/// <summary>
/// Return content processing mode for the symbol
/// </summary>
public object GetParticle ( int symbol ) {
return symbol = = last ? particleLast : particles [ symbol ] ;
}
/// <summary>
/// Output symbol's name
/// </summary>
public string NameOf ( int symbol ) {
foreach ( DictionaryEntry de in names ) {
if ( ( int ) de . Value = = symbol ) {
return ( ( XmlQualifiedName ) de . Key ) . ToString ( ) ;
}
}
if ( wildcards ! = null ) {
foreach ( DictionaryEntry de in wildcards ) {
if ( ( int ) de . Value = = symbol ) {
return ( string ) de . Key + ":*" ;
}
}
}
return "##other:*" ;
}
}
struct Position {
public int symbol ;
public object particle ;
public Position ( int symbol , object particle ) {
this . symbol = symbol ;
this . particle = particle ;
}
}
class Positions {
ArrayList positions = new ArrayList ( ) ;
public int Add ( int symbol , object particle ) {
return positions . Add ( new Position ( symbol , particle ) ) ;
}
public Position this [ int pos ] {
get { return ( Position ) positions [ pos ] ; }
}
public int Count {
get { return positions . Count ; }
}
}
#endregion
#region SystaxTree
/// <summary>
/// Base class for the systax tree nodes
/// </summary>
abstract class SyntaxTreeNode {
/// <summary>
/// Expand NamesapceListNode and RangeNode nodes. All other nodes
/// </summary>
public abstract void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) ;
/// <summary>
/// Clone the syntaxTree. We need to pass symbolsByPosition because leaf nodes have to add themselves to it.
/// </summary>
public abstract SyntaxTreeNode Clone ( Positions positions ) ;
/// <summary>
/// From a regular expression to a DFA
/// Compilers by Aho, Sethi, Ullman.
/// ISBN 0-201-10088-6, p135
/// Construct firstpos, lastpos and calculate followpos
/// </summary>
public abstract void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) ;
/// <summary>
/// Returns nullable property that is being used by ConstructPos
/// </summary>
public abstract bool IsNullable { get ; }
/// <summary>
/// Returns true if node is a range node
/// </summary>
public virtual bool IsRangeNode {
get {
return false ;
}
}
/// <summary>
/// Print syntax tree
/// </summary>
#if DEBUG
public abstract void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) ;
#endif
}
/// <summary>
/// Terminal of the syntax tree
/// </summary>
class LeafNode : SyntaxTreeNode {
int pos ;
public LeafNode ( int pos ) {
this . pos = pos ;
}
public int Pos {
get { return pos ; }
set { pos = value ; }
}
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
// do nothing
}
public override SyntaxTreeNode Clone ( Positions positions ) {
return new LeafNode ( positions . Add ( positions [ pos ] . symbol , positions [ pos ] . particle ) ) ;
}
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
firstpos . Set ( pos ) ;
lastpos . Set ( pos ) ;
}
public override bool IsNullable {
get { return false ; }
}
#if DEBUG
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
bb . Append ( "\"" + symbols . NameOf ( positions [ pos ] . symbol ) + "\"" ) ;
}
#endif
}
/// <summary>
/// Temporary node to represent NamespaceList. Will be expended as a choice of symbols
/// </summary>
class NamespaceListNode : SyntaxTreeNode {
protected NamespaceList namespaceList ;
protected object particle ;
public NamespaceListNode ( NamespaceList namespaceList , object particle ) {
this . namespaceList = namespaceList ;
this . particle = particle ;
}
public override SyntaxTreeNode Clone ( Positions positions ) {
// NamespaceListNode nodes have to be removed prior to that
throw new InvalidOperationException ( ) ;
}
public virtual ICollection GetResolvedSymbols ( SymbolsDictionary symbols ) {
return symbols . GetNamespaceListSymbols ( namespaceList ) ;
}
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
SyntaxTreeNode replacementNode = null ;
foreach ( int symbol in GetResolvedSymbols ( symbols ) ) {
if ( symbols . GetParticle ( symbol ) ! = particle ) {
symbols . IsUpaEnforced = false ;
}
LeafNode node = new LeafNode ( positions . Add ( symbol , particle ) ) ;
if ( replacementNode = = null ) {
replacementNode = node ;
}
else {
InteriorNode choice = new ChoiceNode ( ) ;
choice . LeftChild = replacementNode ;
choice . RightChild = node ;
replacementNode = choice ;
}
}
if ( parent . LeftChild = = this ) {
parent . LeftChild = replacementNode ;
}
else {
parent . RightChild = replacementNode ;
}
}
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
// NamespaceListNode nodes have to be removed prior to that
throw new InvalidOperationException ( ) ;
}
public override bool IsNullable {
// NamespaceListNode nodes have to be removed prior to that
get { throw new InvalidOperationException ( ) ; }
}
#if DEBUG
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
bb . Append ( "[" + namespaceList . ToString ( ) + "]" ) ;
}
#endif
}
/// <summary>
/// Base class for all internal node. Note that only sequence and choice have right child
/// </summary>
abstract class InteriorNode : SyntaxTreeNode {
SyntaxTreeNode leftChild ;
SyntaxTreeNode rightChild ;
public SyntaxTreeNode LeftChild {
get { return leftChild ; }
set { leftChild = value ; }
}
public SyntaxTreeNode RightChild {
get { return rightChild ; }
set { rightChild = value ; }
}
public override SyntaxTreeNode Clone ( Positions positions ) {
InteriorNode other = ( InteriorNode ) this . MemberwiseClone ( ) ;
other . LeftChild = leftChild . Clone ( positions ) ;
if ( rightChild ! = null ) {
other . RightChild = rightChild . Clone ( positions ) ;
}
return other ;
}
//no recursive version of expand tree for Sequence and Choice node
protected void ExpandTreeNoRecursive ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
Stack < InteriorNode > nodeStack = new Stack < InteriorNode > ( ) ;
InteriorNode this_ = this ;
while ( true ) {
if ( this_ . leftChild is ChoiceNode | | this_ . leftChild is SequenceNode ) {
nodeStack . Push ( this_ ) ;
this_ = ( InteriorNode ) this_ . leftChild ;
continue ;
}
this_ . leftChild . ExpandTree ( this_ , symbols , positions ) ;
ProcessRight :
if ( this_ . rightChild ! = null ) {
this_ . rightChild . ExpandTree ( this_ , symbols , positions ) ;
}
if ( nodeStack . Count = = 0 )
break ;
this_ = nodeStack . Pop ( ) ;
goto ProcessRight ;
}
}
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
leftChild . ExpandTree ( this , symbols , positions ) ;
if ( rightChild ! = null ) {
rightChild . ExpandTree ( this , symbols , positions ) ;
}
}
}
sealed class SequenceNode : InteriorNode {
struct SequenceConstructPosContext {
public SequenceNode this_ ;
public BitSet firstpos ;
public BitSet lastpos ;
public BitSet lastposLeft ;
public BitSet firstposRight ;
public SequenceConstructPosContext ( SequenceNode node , BitSet firstpos , BitSet lastpos ) {
this_ = node ;
this . firstpos = firstpos ;
this . lastpos = lastpos ;
lastposLeft = null ;
firstposRight = null ;
}
}
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
Stack < SequenceConstructPosContext > contextStack = new Stack < SequenceConstructPosContext > ( ) ;
SequenceConstructPosContext context = new SequenceConstructPosContext ( this , firstpos , lastpos ) ;
while ( true ) {
SequenceNode this_ = context . this_ ;
context . lastposLeft = new BitSet ( lastpos . Count ) ;
if ( this_ . LeftChild is SequenceNode ) {
contextStack . Push ( context ) ;
context = new SequenceConstructPosContext ( ( SequenceNode ) this_ . LeftChild , context . firstpos , context . lastposLeft ) ;
continue ;
}
this_ . LeftChild . ConstructPos ( context . firstpos , context . lastposLeft , followpos ) ;
ProcessRight :
context . firstposRight = new BitSet ( firstpos . Count ) ;
this_ . RightChild . ConstructPos ( context . firstposRight , context . lastpos , followpos ) ;
if ( this_ . LeftChild . IsNullable & & ! this_ . RightChild . IsRangeNode ) {
context . firstpos . Or ( context . firstposRight ) ;
}
if ( this_ . RightChild . IsNullable ) {
context . lastpos . Or ( context . lastposLeft ) ;
}
for ( int pos = context . lastposLeft . NextSet ( - 1 ) ; pos ! = - 1 ; pos = context . lastposLeft . NextSet ( pos ) ) {
followpos [ pos ] . Or ( context . firstposRight ) ;
}
if ( this_ . RightChild . IsRangeNode ) { //firstpos is leftchild.firstpos as the or with firstposRight has not been done as it is a rangenode
( ( LeafRangeNode ) this_ . RightChild ) . NextIteration = context . firstpos . Clone ( ) ;
}
if ( contextStack . Count = = 0 )
break ;
context = contextStack . Pop ( ) ;
this_ = context . this_ ;
goto ProcessRight ;
}
}
public override bool IsNullable {
get {
SyntaxTreeNode n ;
SequenceNode this_ = this ;
do {
if ( this_ . RightChild . IsRangeNode & & ( ( LeafRangeNode ) this_ . RightChild ) . Min = = 0 )
return true ;
if ( ! this_ . RightChild . IsNullable & & ! this_ . RightChild . IsRangeNode )
return false ;
n = this_ . LeftChild ;
this_ = n as SequenceNode ;
}
while ( this_ ! = null ) ;
return n . IsNullable ;
}
}
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
ExpandTreeNoRecursive ( parent , symbols , positions ) ;
}
#if DEBUG
internal static void WritePos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "FirstPos: " ) ;
WriteBitSet ( firstpos ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "LastPos: " ) ;
WriteBitSet ( lastpos ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "Followpos: " ) ;
for ( int i = 0 ; i < followpos . Length ; i + + ) {
WriteBitSet ( followpos [ i ] ) ;
}
}
internal static void WriteBitSet ( BitSet curpos ) {
int [ ] list = new int [ curpos . Count ] ;
for ( int pos = curpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = curpos . NextSet ( pos ) ) {
list [ pos ] = 1 ;
}
for ( int i = 0 ; i < list . Length ; i + + ) {
Debug . WriteIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , list [ i ] + " " ) ;
}
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "" ) ;
}
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
Stack < SequenceNode > nodeStack = new Stack < SequenceNode > ( ) ;
SequenceNode this_ = this ;
while ( true ) {
bb . Append ( "(" ) ;
if ( this_ . LeftChild is SequenceNode ) {
nodeStack . Push ( this_ ) ;
this_ = ( SequenceNode ) this_ . LeftChild ;
continue ;
}
this_ . LeftChild . Dump ( bb , symbols , positions ) ;
ProcessRight :
bb . Append ( ", " ) ;
this_ . RightChild . Dump ( bb , symbols , positions ) ;
bb . Append ( ")" ) ;
if ( nodeStack . Count = = 0 )
break ;
this_ = nodeStack . Pop ( ) ;
goto ProcessRight ;
}
}
#endif
}
sealed class ChoiceNode : InteriorNode {
private static void ConstructChildPos ( SyntaxTreeNode child , BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
BitSet firstPosTemp = new BitSet ( firstpos . Count ) ;
BitSet lastPosTemp = new BitSet ( lastpos . Count ) ;
child . ConstructPos ( firstPosTemp , lastPosTemp , followpos ) ;
firstpos . Or ( firstPosTemp ) ;
lastpos . Or ( lastPosTemp ) ;
}
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
BitSet firstPosTemp = new BitSet ( firstpos . Count ) ;
BitSet lastPosTemp = new BitSet ( lastpos . Count ) ;
SyntaxTreeNode n ;
ChoiceNode this_ = this ;
do {
ConstructChildPos ( this_ . RightChild , firstPosTemp , lastPosTemp , followpos ) ;
n = this_ . LeftChild ;
this_ = n as ChoiceNode ;
} while ( this_ ! = null ) ;
n . ConstructPos ( firstpos , lastpos , followpos ) ;
firstpos . Or ( firstPosTemp ) ;
lastpos . Or ( lastPosTemp ) ;
}
public override bool IsNullable {
get {
SyntaxTreeNode n ;
ChoiceNode this_ = this ;
do {
if ( this_ . RightChild . IsNullable )
return true ;
n = this_ . LeftChild ;
this_ = n as ChoiceNode ;
}
while ( this_ ! = null ) ;
return n . IsNullable ;
}
}
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
ExpandTreeNoRecursive ( parent , symbols , positions ) ;
}
#if DEBUG
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
Stack < ChoiceNode > nodeStack = new Stack < ChoiceNode > ( ) ;
ChoiceNode this_ = this ;
while ( true ) {
bb . Append ( "(" ) ;
if ( this_ . LeftChild is ChoiceNode ) {
nodeStack . Push ( this_ ) ;
this_ = ( ChoiceNode ) this_ . LeftChild ;
continue ;
}
this_ . LeftChild . Dump ( bb , symbols , positions ) ;
ProcessRight :
bb . Append ( " | " ) ;
this_ . RightChild . Dump ( bb , symbols , positions ) ;
bb . Append ( ")" ) ;
if ( nodeStack . Count = = 0 )
break ;
this_ = nodeStack . Pop ( ) ;
goto ProcessRight ;
}
}
#endif
}
sealed class PlusNode : InteriorNode {
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
LeftChild . ConstructPos ( firstpos , lastpos , followpos ) ;
for ( int pos = lastpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = lastpos . NextSet ( pos ) ) {
followpos [ pos ] . Or ( firstpos ) ;
}
}
public override bool IsNullable {
get { return LeftChild . IsNullable ; }
}
#if DEBUG
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
LeftChild . Dump ( bb , symbols , positions ) ;
bb . Append ( "+" ) ;
}
#endif
}
sealed class QmarkNode : InteriorNode {
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
LeftChild . ConstructPos ( firstpos , lastpos , followpos ) ;
}
public override bool IsNullable {
get { return true ; }
}
#if DEBUG
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
LeftChild . Dump ( bb , symbols , positions ) ;
bb . Append ( "?" ) ;
}
#endif
}
sealed class StarNode : InteriorNode {
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
LeftChild . ConstructPos ( firstpos , lastpos , followpos ) ;
for ( int pos = lastpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = lastpos . NextSet ( pos ) ) {
followpos [ pos ] . Or ( firstpos ) ;
}
}
public override bool IsNullable {
get { return true ; }
}
#if DEBUG
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
LeftChild . Dump ( bb , symbols , positions ) ;
bb . Append ( "*" ) ;
}
#endif
}
#if EXPANDRANGE
/// <summary>
/// Temporary node to occurance range. Will be expended to a sequence of terminals
/// </summary>
sealed class RangeNode : InteriorNode {
int min ;
int max ;
public RangeNode ( int min , int max ) {
this . min = min ;
this . max = max ;
}
public int Max {
get { return max ; }
}
public int Min {
get { return min ; }
}
public override SyntaxTreeNode Clone ( Positions positions ) {
// range nodes have to be removed prior to that
throw new InvalidOperationException ( ) ;
}
/// <summary>
/// Expand tree will replace a{min, max} using following algorithm. Bare in mind that this sequence will have at least two leaves
/// if min == 0 (max cannot be unbounded)
/// a?, ... a?
/// \__ __/
/// max
/// else
/// if max == unbounded
/// a, ... a, a*
/// \__ __/
/// min
/// else
/// a, ... a, a?, ... a?
/// \__ __/ \__ __/
/// min max - min
/// </summary>
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
LeftChild . ExpandTree ( this , symbols , positions ) ;
SyntaxTreeNode replacementNode = null ;
if ( min = = 0 ) {
Debug . Assert ( max ! = int . MaxValue ) ;
replacementNode = NewQmark ( LeftChild ) ;
for ( int i = 0 ; i < max - 1 ; i + + ) {
replacementNode = NewSequence ( replacementNode , NewQmark ( LeftChild . Clone ( positions ) ) ) ;
}
}
else {
replacementNode = LeftChild ;
for ( int i = 0 ; i < min - 1 ; i + + ) {
replacementNode = NewSequence ( replacementNode , LeftChild . Clone ( positions ) ) ;
}
if ( max = = int . MaxValue ) {
replacementNode = NewSequence ( replacementNode , NewStar ( LeftChild . Clone ( positions ) ) ) ;
}
else {
for ( int i = 0 ; i < max - min ; i + + ) {
replacementNode = NewSequence ( replacementNode , NewQmark ( LeftChild . Clone ( positions ) ) ) ;
}
}
}
if ( parent . LeftChild = = this ) {
parent . LeftChild = replacementNode ;
}
else {
parent . RightChild = replacementNode ;
}
}
private SyntaxTreeNode NewSequence ( SyntaxTreeNode leftChild , SyntaxTreeNode rightChild ) {
InteriorNode sequence = new SequenceNode ( ) ;
sequence . LeftChild = leftChild ;
sequence . RightChild = rightChild ;
return sequence ;
}
private SyntaxTreeNode NewStar ( SyntaxTreeNode leftChild ) {
InteriorNode star = new StarNode ( ) ;
star . LeftChild = leftChild ;
return star ;
}
private SyntaxTreeNode NewQmark ( SyntaxTreeNode leftChild ) {
InteriorNode qmark = new QmarkNode ( ) ;
qmark . LeftChild = leftChild ;
return qmark ;
}
public override void ConstructPos ( BitSet firstpos , BitSet lastpos , BitSet [ ] followpos ) {
throw new InvalidOperationException ( ) ;
}
public override bool IsNullable {
get { throw new InvalidOperationException ( ) ; }
}
public override void Dump ( StringBuilder bb , SymbolsDictionary symbols , Positions positions ) {
LeftChild . Dump ( bb , symbols , positions ) ;
bb . Append ( "{" + Convert . ToString ( min , NumberFormatInfo . InvariantInfo ) + ", " + Convert . ToString ( max , NumberFormatInfo . InvariantInfo ) + "}" ) ;
}
}
#endif
/// <summary>
/// Using range node as one of the terminals
/// </summary>
sealed class LeafRangeNode : LeafNode {
decimal min ;
decimal max ;
BitSet nextIteration ;
public LeafRangeNode ( decimal min , decimal max ) : this ( - 1 , min , max ) { }
public LeafRangeNode ( int pos , decimal min , decimal max ) : base ( pos ) {
this . min = min ;
this . max = max ;
}
public decimal Max {
get { return max ; }
}
public decimal Min {
get { return min ; }
}
public BitSet NextIteration {
get {
return nextIteration ;
}
set {
nextIteration = value ;
}
}
public override SyntaxTreeNode Clone ( Positions positions ) {
return new LeafRangeNode ( this . Pos , this . min , this . max ) ;
}
public override bool IsRangeNode {
get {
return true ;
}
}
public override void ExpandTree ( InteriorNode parent , SymbolsDictionary symbols , Positions positions ) {
Debug . Assert ( parent is SequenceNode ) ;
Debug . Assert ( this = = parent . RightChild ) ;
//change the range node min to zero if left is nullable
if ( parent . LeftChild . IsNullable ) {
min = 0 ;
}
}
}
#endregion
#region ContentValidator
/// <summary>
/// Basic ContentValidator
/// </summary>
class ContentValidator {
XmlSchemaContentType contentType ;
bool isOpen ; //For XDR Content Models or ANY
bool isEmptiable ;
public static readonly ContentValidator Empty = new ContentValidator ( XmlSchemaContentType . Empty ) ;
public static readonly ContentValidator TextOnly = new ContentValidator ( XmlSchemaContentType . TextOnly , false , false ) ;
public static readonly ContentValidator Mixed = new ContentValidator ( XmlSchemaContentType . Mixed ) ;
public static readonly ContentValidator Any = new ContentValidator ( XmlSchemaContentType . Mixed , true , true ) ;
public ContentValidator ( XmlSchemaContentType contentType ) {
this . contentType = contentType ;
this . isEmptiable = true ;
}
protected ContentValidator ( XmlSchemaContentType contentType , bool isOpen , bool isEmptiable ) {
this . contentType = contentType ;
this . isOpen = isOpen ;
this . isEmptiable = isEmptiable ;
}
public XmlSchemaContentType ContentType {
get { return contentType ; }
}
public bool PreserveWhitespace {
get { return contentType = = XmlSchemaContentType . TextOnly | | contentType = = XmlSchemaContentType . Mixed ; }
}
public virtual bool IsEmptiable {
get { return isEmptiable ; }
}
public bool IsOpen {
get {
if ( contentType = = XmlSchemaContentType . TextOnly | | contentType = = XmlSchemaContentType . Empty )
return false ;
else
return isOpen ;
}
set { isOpen = value ; }
}
public virtual void InitValidation ( ValidationState context ) {
// do nothin'
}
public virtual object ValidateElement ( XmlQualifiedName name , ValidationState context , out int errorCode ) {
if ( contentType = = XmlSchemaContentType . TextOnly | | contentType = = XmlSchemaContentType . Empty ) { //Cannot have elements in TextOnly or Empty content
context . NeedValidateChildren = false ;
}
errorCode = - 1 ;
return null ;
}
public virtual bool CompleteValidation ( ValidationState context ) {
return true ;
}
public virtual ArrayList ExpectedElements ( ValidationState context , bool isRequiredOnly ) {
return null ;
}
public virtual ArrayList ExpectedParticles ( ValidationState context , bool isRequiredOnly , XmlSchemaSet schemaSet ) {
return null ;
}
public static void AddParticleToExpected ( XmlSchemaParticle p , XmlSchemaSet schemaSet , ArrayList particles ) {
AddParticleToExpected ( p , schemaSet , particles , false ) ;
}
public static void AddParticleToExpected ( XmlSchemaParticle p , XmlSchemaSet schemaSet , ArrayList particles , bool global ) {
if ( ! particles . Contains ( p ) ) {
particles . Add ( p ) ;
}
//Only then it can be head of substitutionGrp, if it is, add its members
XmlSchemaElement elem = p as XmlSchemaElement ;
if ( elem ! = null & & ( global | | ! elem . RefName . IsEmpty ) ) {
XmlSchemaObjectTable substitutionGroups = schemaSet . SubstitutionGroups ;
XmlSchemaSubstitutionGroup grp = ( XmlSchemaSubstitutionGroup ) substitutionGroups [ elem . QualifiedName ] ;
if ( grp ! = null ) {
//Grp members wil contain the head as well, so filter head as we added it already
for ( int i = 0 ; i < grp . Members . Count ; + + i ) {
XmlSchemaElement member = ( XmlSchemaElement ) grp . Members [ i ] ;
if ( ! elem . QualifiedName . Equals ( member . QualifiedName ) & & ! particles . Contains ( member ) ) { //A member might have been directly present as an element in the content model
particles . Add ( member ) ;
}
}
}
}
}
}
sealed class ParticleContentValidator : ContentValidator {
SymbolsDictionary symbols ;
Positions positions ;
Stack stack ; // parsing context
SyntaxTreeNode contentNode ; // content model points to syntax tree
bool isPartial ; // whether the closure applies to partial or the whole node that is on top of the stack
int minMaxNodesCount ;
bool enableUpaCheck ;
public ParticleContentValidator ( XmlSchemaContentType contentType ) : this ( contentType , true ) {
}
public ParticleContentValidator ( XmlSchemaContentType contentType , bool enableUpaCheck ) : base ( contentType ) {
this . enableUpaCheck = enableUpaCheck ;
}
public override void InitValidation ( ValidationState context ) {
// ParticleContentValidator cannot be used during validation
throw new InvalidOperationException ( ) ;
}
public override object ValidateElement ( XmlQualifiedName name , ValidationState context , out int errorCode ) {
// ParticleContentValidator cannot be used during validation
throw new InvalidOperationException ( ) ;
}
public override bool CompleteValidation ( ValidationState context ) {
// ParticleContentValidator cannot be used during validation
throw new InvalidOperationException ( ) ;
}
public void Start ( ) {
symbols = new SymbolsDictionary ( ) ;
positions = new Positions ( ) ;
stack = new Stack ( ) ;
}
public void OpenGroup ( ) {
stack . Push ( null ) ;
}
public void CloseGroup ( ) {
SyntaxTreeNode node = ( SyntaxTreeNode ) stack . Pop ( ) ;
if ( node = = null ) {
return ;
}
if ( stack . Count = = 0 ) {
contentNode = node ;
isPartial = false ;
}
else {
// some collapsing to do...
InteriorNode inNode = ( InteriorNode ) stack . Pop ( ) ;
if ( inNode ! = null ) {
inNode . RightChild = node ;
node = inNode ;
isPartial = true ;
}
else {
isPartial = false ;
}
stack . Push ( node ) ;
}
}
public bool Exists ( XmlQualifiedName name ) {
if ( symbols . Exists ( name ) ) {
return true ;
}
return false ;
}
public void AddName ( XmlQualifiedName name , object particle ) {
AddLeafNode ( new LeafNode ( positions . Add ( symbols . AddName ( name , particle ) , particle ) ) ) ;
}
public void AddNamespaceList ( NamespaceList namespaceList , object particle ) {
symbols . AddNamespaceList ( namespaceList , particle , false ) ;
AddLeafNode ( new NamespaceListNode ( namespaceList , particle ) ) ;
}
private void AddLeafNode ( SyntaxTreeNode node ) {
if ( stack . Count > 0 ) {
InteriorNode inNode = ( InteriorNode ) stack . Pop ( ) ;
if ( inNode ! = null ) {
inNode . RightChild = node ;
node = inNode ;
}
}
stack . Push ( node ) ;
isPartial = true ;
}
public void AddChoice ( ) {
SyntaxTreeNode node = ( SyntaxTreeNode ) stack . Pop ( ) ;
InteriorNode choice = new ChoiceNode ( ) ;
choice . LeftChild = node ;
stack . Push ( choice ) ;
}
public void AddSequence ( ) {
SyntaxTreeNode node = ( SyntaxTreeNode ) stack . Pop ( ) ;
InteriorNode sequence = new SequenceNode ( ) ;
sequence . LeftChild = node ;
stack . Push ( sequence ) ;
}
public void AddStar ( ) {
Closure ( new StarNode ( ) ) ;
}
public void AddPlus ( ) {
Closure ( new PlusNode ( ) ) ;
}
public void AddQMark ( ) {
Closure ( new QmarkNode ( ) ) ;
}
public void AddLeafRange ( decimal min , decimal max ) {
LeafRangeNode rNode = new LeafRangeNode ( min , max ) ;
int pos = positions . Add ( - 2 , rNode ) ;
rNode . Pos = pos ;
InteriorNode sequence = new SequenceNode ( ) ;
sequence . RightChild = rNode ;
Closure ( sequence ) ;
minMaxNodesCount + + ;
}
#if EXPANDRANGE
public void AddRange ( int min , int max ) {
Closure ( new RangeNode ( min , max ) ) ;
}
#endif
private void Closure ( InteriorNode node ) {
if ( stack . Count > 0 ) {
SyntaxTreeNode topNode = ( SyntaxTreeNode ) stack . Pop ( ) ;
InteriorNode inNode = topNode as InteriorNode ;
if ( isPartial & & inNode ! = null ) {
// need to reach in and wrap right hand side of element.
// and n remains the same.
node . LeftChild = inNode . RightChild ;
inNode . RightChild = node ;
}
else {
// wrap terminal or any node
node . LeftChild = topNode ;
topNode = node ;
}
stack . Push ( topNode ) ;
}
else if ( contentNode ! = null ) { //If there is content to wrap
// wrap whole content
node . LeftChild = contentNode ;
contentNode = node ;
}
}
public ContentValidator Finish ( ) {
return Finish ( true ) ;
}
public ContentValidator Finish ( bool useDFA ) {
Debug . Assert ( ContentType = = XmlSchemaContentType . ElementOnly | | ContentType = = XmlSchemaContentType . Mixed ) ;
if ( contentNode = = null ) {
if ( ContentType = = XmlSchemaContentType . Mixed ) {
string ctype = IsOpen ? "Any" : "TextOnly" ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "\t\t\tContentType: " + ctype ) ;
return IsOpen ? ContentValidator . Any : ContentValidator . TextOnly ;
}
else {
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "\t\t\tContent: EMPTY" ) ;
Debug . Assert ( ! IsOpen ) ;
return ContentValidator . Empty ;
}
}
#if DEBUG
StringBuilder bb = new StringBuilder ( ) ;
contentNode . Dump ( bb , symbols , positions ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "\t\t\tContent : " + bb . ToString ( ) ) ;
#endif
// Add end marker
InteriorNode contentRoot = new SequenceNode ( ) ;
contentRoot . LeftChild = contentNode ;
LeafNode endMarker = new LeafNode ( positions . Add ( symbols . AddName ( XmlQualifiedName . Empty , null ) , null ) ) ;
contentRoot . RightChild = endMarker ;
// Eliminate NamespaceListNode(s) and RangeNode(s)
contentNode . ExpandTree ( contentRoot , symbols , positions ) ;
#if DEBUG
bb = new StringBuilder ( ) ;
contentRoot . LeftChild . Dump ( bb , symbols , positions ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "\t\t\tExpended: " + bb . ToString ( ) ) ;
#endif
// calculate followpos
int symbolsCount = symbols . Count ;
int positionsCount = positions . Count ;
BitSet firstpos = new BitSet ( positionsCount ) ;
BitSet lastpos = new BitSet ( positionsCount ) ;
BitSet [ ] followpos = new BitSet [ positionsCount ] ;
for ( int i = 0 ; i < positionsCount ; i + + ) {
followpos [ i ] = new BitSet ( positionsCount ) ;
}
contentRoot . ConstructPos ( firstpos , lastpos , followpos ) ;
#if DEBUG
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "firstpos, lastpos, followpos" ) ;
SequenceNode . WritePos ( firstpos , lastpos , followpos ) ;
#endif
if ( minMaxNodesCount > 0 ) { //If the tree has any terminal range nodes
BitSet positionsWithRangeTerminals ;
BitSet [ ] minMaxFollowPos = CalculateTotalFollowposForRangeNodes ( firstpos , followpos , out positionsWithRangeTerminals ) ;
if ( enableUpaCheck ) {
CheckCMUPAWithLeafRangeNodes ( GetApplicableMinMaxFollowPos ( firstpos , positionsWithRangeTerminals , minMaxFollowPos ) ) ;
for ( int i = 0 ; i < positionsCount ; i + + ) {
CheckCMUPAWithLeafRangeNodes ( GetApplicableMinMaxFollowPos ( followpos [ i ] , positionsWithRangeTerminals , minMaxFollowPos ) ) ;
}
}
return new RangeContentValidator ( firstpos , followpos , symbols , positions , endMarker . Pos , this . ContentType , contentRoot . LeftChild . IsNullable , positionsWithRangeTerminals , minMaxNodesCount ) ;
}
else {
int [ ] [ ] transitionTable = null ;
// if each symbol has unique particle we are golden
if ( ! symbols . IsUpaEnforced ) {
if ( enableUpaCheck ) {
// multiple positions that match the same symbol have different particles, but they never follow the same position
CheckUniqueParticleAttribution ( firstpos , followpos ) ;
}
}
else if ( useDFA ) {
// Can return null if the number of states reaches higher than 8192 / positionsCount
transitionTable = BuildTransitionTable ( firstpos , followpos , endMarker . Pos ) ;
}
#if DEBUG
bb = new StringBuilder ( ) ;
Dump ( bb , followpos , transitionTable ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , bb . ToString ( ) ) ;
#endif
if ( transitionTable ! = null ) {
return new DfaContentValidator ( transitionTable , symbols , this . ContentType , this . IsOpen , contentRoot . LeftChild . IsNullable ) ;
} else {
return new NfaContentValidator ( firstpos , followpos , symbols , positions , endMarker . Pos , this . ContentType , this . IsOpen , contentRoot . LeftChild . IsNullable ) ;
}
}
}
private BitSet [ ] CalculateTotalFollowposForRangeNodes ( BitSet firstpos , BitSet [ ] followpos , out BitSet posWithRangeTerminals ) {
int positionsCount = positions . Count ; //terminals
posWithRangeTerminals = new BitSet ( positionsCount ) ;
//Compute followpos for each range node
//For any range node that is surrounded by an outer range node, its follow positions will include those of the outer range node
BitSet [ ] minmaxFollowPos = new BitSet [ minMaxNodesCount ] ;
int localMinMaxNodesCount = 0 ;
for ( int i = positionsCount - 1 ; i > = 0 ; i - - ) {
Position p = positions [ i ] ;
if ( p . symbol = = - 2 ) { //P is a LeafRangeNode
LeafRangeNode lrNode = p . particle as LeafRangeNode ;
Debug . Assert ( lrNode ! = null ) ;
BitSet tempFollowPos = new BitSet ( positionsCount ) ;
tempFollowPos . Clear ( ) ;
tempFollowPos . Or ( followpos [ i ] ) ; //Add the followpos of the range node
if ( lrNode . Min ! = lrNode . Max ) { //If they are the same, then followpos cannot include the firstpos
tempFollowPos . Or ( lrNode . NextIteration ) ; //Add the nextIteration of the range node (this is the firstpos of its parent's leftChild)
}
//For each position in the bitset, if it is a outer range node (pos > i), then add its followpos as well to the current node's followpos
for ( int pos = tempFollowPos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = tempFollowPos . NextSet ( pos ) ) {
if ( pos > i ) {
Position p1 = positions [ pos ] ;
if ( p1 . symbol = = - 2 ) {
LeafRangeNode lrNode1 = p1 . particle as LeafRangeNode ;
Debug . Assert ( lrNode1 ! = null ) ;
tempFollowPos . Or ( minmaxFollowPos [ lrNode1 . Pos ] ) ;
}
}
}
//set the followpos built to the index in the BitSet[]
minmaxFollowPos [ localMinMaxNodesCount ] = tempFollowPos ;
lrNode . Pos = localMinMaxNodesCount + + ;
posWithRangeTerminals . Set ( i ) ;
}
}
return minmaxFollowPos ;
}
private void CheckCMUPAWithLeafRangeNodes ( BitSet curpos ) {
object [ ] symbolMatches = new object [ symbols . Count ] ;
for ( int pos = curpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = curpos . NextSet ( pos ) ) {
Position currentPosition = positions [ pos ] ;
int symbol = currentPosition . symbol ;
if ( symbol > = 0 ) { //its not a range position
if ( symbolMatches [ symbol ] ! = null ) {
throw new UpaException ( symbolMatches [ symbol ] , currentPosition . particle ) ;
}
else {
symbolMatches [ symbol ] = currentPosition . particle ;
}
}
}
}
//For each position, this method calculates the additional follows of any range nodes that need to be added to its followpos
//((ab?)2-4)c, Followpos of a is b as well as that of node R(2-4) = c
private BitSet GetApplicableMinMaxFollowPos ( BitSet curpos , BitSet posWithRangeTerminals , BitSet [ ] minmaxFollowPos ) {
if ( curpos . Intersects ( posWithRangeTerminals ) ) {
BitSet newSet = new BitSet ( positions . Count ) ; //Doing work again
newSet . Or ( curpos ) ;
newSet . And ( posWithRangeTerminals ) ;
curpos = curpos . Clone ( ) ;
for ( int pos = newSet . NextSet ( - 1 ) ; pos ! = - 1 ; pos = newSet . NextSet ( pos ) ) {
LeafRangeNode lrNode = positions [ pos ] . particle as LeafRangeNode ;
curpos . Or ( minmaxFollowPos [ lrNode . Pos ] ) ;
}
}
return curpos ;
}
private void CheckUniqueParticleAttribution ( BitSet firstpos , BitSet [ ] followpos ) {
CheckUniqueParticleAttribution ( firstpos ) ;
for ( int i = 0 ; i < positions . Count ; i + + ) {
CheckUniqueParticleAttribution ( followpos [ i ] ) ;
}
}
private void CheckUniqueParticleAttribution ( BitSet curpos ) {
// particles will be attributed uniquely if the same symbol never poins to two different ones
object [ ] particles = new object [ symbols . Count ] ;
for ( int pos = curpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = curpos . NextSet ( pos ) ) {
// if position can follow
int symbol = positions [ pos ] . symbol ;
if ( particles [ symbol ] = = null ) {
// set particle for the symbol
particles [ symbol ] = positions [ pos ] . particle ;
}
else if ( particles [ symbol ] ! = positions [ pos ] . particle ) {
throw new UpaException ( particles [ symbol ] , positions [ pos ] . particle ) ;
}
// two different position point to the same symbol and particle - that's OK
}
}
/// <summary>
/// Algorithm 3.5 Construction of a DFA from a regular expression
/// </summary>
private int [ ] [ ] BuildTransitionTable ( BitSet firstpos , BitSet [ ] followpos , int endMarkerPos ) {
const int TimeConstant = 8192 ; //(MaxStates * MaxPositions should be a constant)
int positionsCount = positions . Count ;
int MaxStatesCount = TimeConstant / positionsCount ;
int symbolsCount = symbols . Count ;
// transition table (Dtran in the book)
ArrayList transitionTable = new ArrayList ( ) ;
// state lookup table (Dstate in the book)
Hashtable stateTable = new Hashtable ( ) ;
// Add empty set that would signal an error
stateTable . Add ( new BitSet ( positionsCount ) , - 1 ) ;
// lists unmarked states
Queue unmarked = new Queue ( ) ;
// initially, the only unmarked state in Dstates is firstpo(root)
int state = 0 ;
unmarked . Enqueue ( firstpos ) ;
stateTable . Add ( firstpos , 0 ) ;
transitionTable . Add ( new int [ symbolsCount + 1 ] ) ;
// while there is an umnarked state T in Dstates do begin
while ( unmarked . Count > 0 ) {
BitSet statePosSet = ( BitSet ) unmarked . Dequeue ( ) ; // all positions that constitute DFA state
Debug . Assert ( state = = ( int ) stateTable [ statePosSet ] ) ; // just make sure that statePosSet is for correct state
int [ ] transition = ( int [ ] ) transitionTable [ state ] ;
if ( statePosSet [ endMarkerPos ] ) {
transition [ symbolsCount ] = 1 ; // accepting
}
// for each input symbol a do begin
for ( int symbol = 0 ; symbol < symbolsCount ; symbol + + ) {
// let U be the set of positions that are in followpos(p)
// for some position p in T
// such that the symbol at position p is a
BitSet newset = new BitSet ( positionsCount ) ;
for ( int pos = statePosSet . NextSet ( - 1 ) ; pos ! = - 1 ; pos = statePosSet . NextSet ( pos ) ) {
if ( symbol = = positions [ pos ] . symbol ) {
newset . Or ( followpos [ pos ] ) ;
}
}
// if U is not empty and is not in Dstates then
// add U as an unmarked state to Dstates
object lookup = stateTable [ newset ] ;
if ( lookup ! = null ) {
transition [ symbol ] = ( int ) lookup ;
}
else {
// construct new state
int newState = stateTable . Count - 1 ;
if ( newState > = MaxStatesCount ) {
return null ;
}
unmarked . Enqueue ( newset ) ;
stateTable . Add ( newset , newState ) ;
transitionTable . Add ( new int [ symbolsCount + 1 ] ) ;
transition [ symbol ] = newState ;
}
}
state + + ;
}
// now convert transition table to array
return ( int [ ] [ ] ) transitionTable . ToArray ( typeof ( int [ ] ) ) ;
}
#if DEBUG
private void Dump ( StringBuilder bb , BitSet [ ] followpos , int [ ] [ ] transitionTable ) {
// Temporary printout
bb . AppendLine ( "Positions" ) ;
for ( int i = 0 ; i < positions . Count ; i + + ) {
bb . AppendLine ( i + " " + positions [ i ] . symbol . ToString ( NumberFormatInfo . InvariantInfo ) + " " + symbols . NameOf ( positions [ i ] . symbol ) ) ;
}
bb . AppendLine ( "Followpos" ) ;
for ( int i = 0 ; i < positions . Count ; i + + ) {
for ( int j = 0 ; j < positions . Count ; j + + ) {
bb . Append ( followpos [ i ] [ j ] ? "X" : "O" ) ;
}
bb . AppendLine ( ) ;
}
if ( transitionTable ! = null ) {
// Temporary printout
bb . AppendLine ( "Transitions" ) ;
for ( int i = 0 ; i < transitionTable . Length ; i + + ) {
for ( int j = 0 ; j < symbols . Count ; j + + ) {
if ( transitionTable [ i ] [ j ] = = - 1 ) {
bb . Append ( " x " ) ;
}
else {
bb . AppendFormat ( " {0:000} " , transitionTable [ i ] [ j ] ) ;
}
}
bb . AppendLine ( transitionTable [ i ] [ symbols . Count ] = = 1 ? "+" : "" ) ;
}
}
}
#endif
}
/// <summary>
/// Deterministic Finite Automata
/// Compilers by Aho, Sethi, Ullman.
/// ISBN 0-201-10088-6, pp. 115, 116, 140
/// </summary>
sealed class DfaContentValidator : ContentValidator {
int [ ] [ ] transitionTable ;
SymbolsDictionary symbols ;
/// <summary>
/// Algorithm 3.5 Construction of a DFA from a regular expression
/// </summary>
internal DfaContentValidator (
int [ ] [ ] transitionTable , SymbolsDictionary symbols ,
XmlSchemaContentType contentType , bool isOpen , bool isEmptiable ) : base ( contentType , isOpen , isEmptiable ) {
this . transitionTable = transitionTable ;
this . symbols = symbols ;
}
public override void InitValidation ( ValidationState context ) {
context . CurrentState . State = 0 ;
context . HasMatched = transitionTable [ 0 ] [ symbols . Count ] > 0 ;
}
/// <summary>
/// Algorithm 3.1 Simulating a DFA
/// </summary>
public override object ValidateElement ( XmlQualifiedName name , ValidationState context , out int errorCode ) {
int symbol = symbols [ name ] ;
int state = transitionTable [ context . CurrentState . State ] [ symbol ] ;
errorCode = 0 ;
if ( state ! = - 1 ) {
context . CurrentState . State = state ;
context . HasMatched = transitionTable [ context . CurrentState . State ] [ symbols . Count ] > 0 ;
return symbols . GetParticle ( symbol ) ; // OK
}
if ( IsOpen & & context . HasMatched ) {
// XDR allows any well-formed contents after matched.
return null ;
}
context . NeedValidateChildren = false ;
errorCode = - 1 ;
return null ; // will never be here
}
public override bool CompleteValidation ( ValidationState context ) {
if ( ! context . HasMatched ) {
return false ;
}
return true ;
}
public override ArrayList ExpectedElements ( ValidationState context , bool isRequiredOnly ) {
ArrayList names = null ;
int [ ] transition = transitionTable [ context . CurrentState . State ] ;
if ( transition ! = null ) {
for ( int i = 0 ; i < transition . Length - 1 ; i + + ) {
if ( transition [ i ] ! = - 1 ) {
if ( names = = null ) {
names = new ArrayList ( ) ;
}
XmlSchemaParticle p = ( XmlSchemaParticle ) symbols . GetParticle ( i ) ;
if ( p = = null ) {
string s = symbols . NameOf ( i ) ;
if ( s . Length ! = 0 ) {
names . Add ( s ) ;
}
}
else {
string s = p . NameString ;
if ( ! names . Contains ( s ) ) {
names . Add ( s ) ;
}
}
}
}
}
return names ;
}
public override ArrayList ExpectedParticles ( ValidationState context , bool isRequiredOnly , XmlSchemaSet schemaSet ) {
ArrayList particles = new ArrayList ( ) ;
int [ ] transition = transitionTable [ context . CurrentState . State ] ;
if ( transition ! = null ) {
for ( int i = 0 ; i < transition . Length - 1 ; i + + ) {
if ( transition [ i ] ! = - 1 ) {
XmlSchemaParticle p = ( XmlSchemaParticle ) symbols . GetParticle ( i ) ;
if ( p = = null ) {
continue ;
}
AddParticleToExpected ( p , schemaSet , particles ) ;
}
}
}
return particles ;
}
}
/// <summary>
/// Nondeterministic Finite Automata
/// Compilers by Aho, Sethi, Ullman.
/// ISBN 0-201-10088-6, pp. 126,140
/// </summary>
sealed class NfaContentValidator : ContentValidator {
BitSet firstpos ;
BitSet [ ] followpos ;
SymbolsDictionary symbols ;
Positions positions ;
int endMarkerPos ;
internal NfaContentValidator (
BitSet firstpos , BitSet [ ] followpos , SymbolsDictionary symbols , Positions positions , int endMarkerPos ,
XmlSchemaContentType contentType , bool isOpen , bool isEmptiable ) : base ( contentType , isOpen , isEmptiable ) {
this . firstpos = firstpos ;
this . followpos = followpos ;
this . symbols = symbols ;
this . positions = positions ;
this . endMarkerPos = endMarkerPos ;
}
public override void InitValidation ( ValidationState context ) {
context . CurPos [ 0 ] = firstpos . Clone ( ) ;
context . CurPos [ 1 ] = new BitSet ( firstpos . Count ) ;
context . CurrentState . CurPosIndex = 0 ;
}
/// <summary>
/// Algorithm 3.4 Simulation of an NFA
/// </summary>
public override object ValidateElement ( XmlQualifiedName name , ValidationState context , out int errorCode ) {
BitSet curpos = context . CurPos [ context . CurrentState . CurPosIndex ] ;
int next = ( context . CurrentState . CurPosIndex + 1 ) % 2 ;
BitSet nextpos = context . CurPos [ next ] ;
nextpos . Clear ( ) ;
int symbol = symbols [ name ] ;
object particle = null ;
errorCode = 0 ;
for ( int pos = curpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = curpos . NextSet ( pos ) ) {
// if position can follow
if ( symbol = = positions [ pos ] . symbol ) {
nextpos . Or ( followpos [ pos ] ) ;
particle = positions [ pos ] . particle ; //Between element and wildcard, element will be in earlier pos than wildcard since we add the element nodes to the list of positions first
break ; // and then ExpandTree for the namespace nodes which adds the wildcards to the positions list
}
}
if ( ! nextpos . IsEmpty ) {
context . CurrentState . CurPosIndex = next ;
return particle ;
}
if ( IsOpen & & curpos [ endMarkerPos ] ) {
// XDR allows any well-formed contents after matched.
return null ;
}
context . NeedValidateChildren = false ;
errorCode = - 1 ;
return null ; // will never be here
}
#if FINDUPA_PARTICLE
private bool FindUPAParticle ( ref object originalParticle , object newParticle ) {
if ( originalParticle = = null ) {
originalParticle = newParticle ;
if ( originalParticle is XmlSchemaElement ) { //if the first particle is element, then break, otherwise try to find an element
return true ;
}
}
else if ( newParticle is XmlSchemaElement ) {
if ( originalParticle is XmlSchemaAny ) { //Weak wildcards, element takes precendence over any
originalParticle = newParticle ;
return true ;
}
}
return false ;
}
#endif
public override bool CompleteValidation ( ValidationState context ) {
if ( ! context . CurPos [ context . CurrentState . CurPosIndex ] [ endMarkerPos ] ) {
return false ;
}
return true ;
}
public override ArrayList ExpectedElements ( ValidationState context , bool isRequiredOnly ) {
ArrayList names = null ;
BitSet curpos = context . CurPos [ context . CurrentState . CurPosIndex ] ;
for ( int pos = curpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = curpos . NextSet ( pos ) ) {
if ( names = = null ) {
names = new ArrayList ( ) ;
}
XmlSchemaParticle p = ( XmlSchemaParticle ) positions [ pos ] . particle ;
if ( p = = null ) {
string s = symbols . NameOf ( positions [ pos ] . symbol ) ;
if ( s . Length ! = 0 ) {
names . Add ( s ) ;
}
}
else {
string s = p . NameString ;
if ( ! names . Contains ( s ) ) {
names . Add ( s ) ;
}
}
}
return names ;
}
public override ArrayList ExpectedParticles ( ValidationState context , bool isRequiredOnly , XmlSchemaSet schemaSet ) {
ArrayList particles = new ArrayList ( ) ;
BitSet curpos = context . CurPos [ context . CurrentState . CurPosIndex ] ;
for ( int pos = curpos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = curpos . NextSet ( pos ) ) {
XmlSchemaParticle p = ( XmlSchemaParticle ) positions [ pos ] . particle ;
if ( p = = null ) {
continue ;
}
else {
AddParticleToExpected ( p , schemaSet , particles ) ;
}
}
return particles ;
}
}
internal struct RangePositionInfo {
public BitSet curpos ;
public decimal [ ] rangeCounters ;
}
sealed class RangeContentValidator : ContentValidator {
BitSet firstpos ;
BitSet [ ] followpos ;
BitSet positionsWithRangeTerminals ;
SymbolsDictionary symbols ;
Positions positions ;
int minMaxNodesCount ;
int endMarkerPos ;
internal RangeContentValidator (
BitSet firstpos , BitSet [ ] followpos , SymbolsDictionary symbols , Positions positions , int endMarkerPos , XmlSchemaContentType contentType , bool isEmptiable , BitSet positionsWithRangeTerminals , int minmaxNodesCount ) : base ( contentType , false , isEmptiable ) {
this . firstpos = firstpos ;
this . followpos = followpos ;
this . symbols = symbols ;
this . positions = positions ;
this . positionsWithRangeTerminals = positionsWithRangeTerminals ;
this . minMaxNodesCount = minmaxNodesCount ;
this . endMarkerPos = endMarkerPos ;
}
public override void InitValidation ( ValidationState context ) {
int positionsCount = positions . Count ;
List < RangePositionInfo > runningPositions = context . RunningPositions ;
if ( runningPositions ! = null ) {
Debug . Assert ( minMaxNodesCount ! = 0 ) ;
runningPositions . Clear ( ) ;
}
else {
runningPositions = new List < RangePositionInfo > ( ) ;
context . RunningPositions = runningPositions ;
}
RangePositionInfo rposInfo = new RangePositionInfo ( ) ;
rposInfo . curpos = firstpos . Clone ( ) ;
rposInfo . rangeCounters = new decimal [ minMaxNodesCount ] ;
runningPositions . Add ( rposInfo ) ;
context . CurrentState . NumberOfRunningPos = 1 ;
context . HasMatched = rposInfo . curpos . Get ( endMarkerPos ) ;
}
public override object ValidateElement ( XmlQualifiedName name , ValidationState context , out int errorCode ) {
errorCode = 0 ;
int symbol = symbols [ name ] ;
bool hasSeenFinalPosition = false ;
List < RangePositionInfo > runningPositions = context . RunningPositions ;
int matchCount = context . CurrentState . NumberOfRunningPos ;
int k = 0 ;
RangePositionInfo rposInfo ;
int pos = - 1 ;
int firstMatchedIndex = - 1 ;
bool matched = false ;
#if RANGE_DEBUG
WriteRunningPositions ( "Current running positions to match" , runningPositions , name , matchCount ) ;
#endif
while ( k < matchCount ) { //we are looking for the first match in the list of bitsets
rposInfo = runningPositions [ k ] ;
BitSet curpos = rposInfo . curpos ;
for ( int matchpos = curpos . NextSet ( - 1 ) ; matchpos ! = - 1 ; matchpos = curpos . NextSet ( matchpos ) ) { //In all sets, have to scan all positions because of Disabled UPA possibility
if ( symbol = = positions [ matchpos ] . symbol ) {
pos = matchpos ;
if ( firstMatchedIndex = = - 1 ) { // get the first match for this symbol
firstMatchedIndex = k ;
}
matched = true ;
break ;
}
}
if ( matched & & positions [ pos ] . particle is XmlSchemaElement ) { //We found a match in the list, break at that bitset
break ;
}
else {
k + + ;
}
}
if ( k = = matchCount & & pos ! = - 1 ) { // we did find a match but that was any and hence continued ahead for element
k = firstMatchedIndex ;
}
if ( k < matchCount ) { //There is a match
if ( k ! = 0 ) { //If the first bitset itself matched, then no need to remove anything
#if RANGE_DEBUG
WriteRunningPositions ( "Removing unmatched entries till the first match" , runningPositions , XmlQualifiedName . Empty , k ) ;
#endif
runningPositions . RemoveRange ( 0 , k ) ; //Delete entries from 0 to k-1
}
matchCount = matchCount - k ;
k = 0 ; // Since we re-sized the array
while ( k < matchCount ) {
rposInfo = runningPositions [ k ] ;
matched = rposInfo . curpos . Get ( pos ) ; //Look for the bitset that matches the same position as pos
if ( matched ) { //If match found, get the follow positions of the current matched position
#if RANGE_DEBUG
Debug . WriteIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "Matched position: " + pos + " " ) ; SequenceNode . WriteBitSet ( rposInfo . curpos ) ;
Debug . WriteIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "Follow pos of Matched position: " ) ; SequenceNode . WriteBitSet ( followpos [ pos ] ) ;
#endif
rposInfo . curpos = followpos [ pos ] ; //Note that we are copying the same counters of the current position to that of the follow position
runningPositions [ k ] = rposInfo ;
k + + ;
}
else { //Clear the current pos and get another position from the list to start matching
matchCount - - ;
if ( matchCount > 0 ) {
RangePositionInfo lastrpos = runningPositions [ matchCount ] ;
runningPositions [ matchCount ] = runningPositions [ k ] ;
runningPositions [ k ] = lastrpos ;
}
}
}
}
else { //There is no match
matchCount = 0 ;
}
if ( matchCount > 0 ) {
Debug . Assert ( minMaxNodesCount > 0 ) ;
if ( matchCount > = 10000 ) {
context . TooComplex = true ;
matchCount / = 2 ;
}
#if RANGE_DEBUG
WriteRunningPositions ( "Matched positions to expand " , runningPositions , name , matchCount ) ;
#endif
for ( k = matchCount - 1 ; k > = 0 ; k - - ) {
int j = k ;
BitSet currentRunningPosition = runningPositions [ k ] . curpos ;
hasSeenFinalPosition = hasSeenFinalPosition | | currentRunningPosition . Get ( endMarkerPos ) ; //Accepting position reached if the current position BitSet contains the endPosition
while ( matchCount < 10000 & & currentRunningPosition . Intersects ( positionsWithRangeTerminals ) ) {
//Now might add 2 more positions to followpos
//1. nextIteration of the rangeNode, which is firstpos of its parent's leftChild
//2. Followpos of the range node
BitSet countingPosition = currentRunningPosition . Clone ( ) ;
countingPosition . And ( positionsWithRangeTerminals ) ;
int cPos = countingPosition . NextSet ( - 1 ) ; //Get the first position where leaf range node appears
LeafRangeNode lrNode = positions [ cPos ] . particle as LeafRangeNode ; //For a position with leaf range node, the particle is the node itself
Debug . Assert ( lrNode ! = null ) ;
rposInfo = runningPositions [ j ] ;
if ( matchCount + 2 > = runningPositions . Count ) {
runningPositions . Add ( new RangePositionInfo ( ) ) ;
runningPositions . Add ( new RangePositionInfo ( ) ) ;
}
RangePositionInfo newRPosInfo = runningPositions [ matchCount ] ;
if ( newRPosInfo . rangeCounters = = null ) {
newRPosInfo . rangeCounters = new decimal [ minMaxNodesCount ] ;
}
Array . Copy ( rposInfo . rangeCounters , 0 , newRPosInfo . rangeCounters , 0 , rposInfo . rangeCounters . Length ) ;
decimal count = + + newRPosInfo . rangeCounters [ lrNode . Pos ] ;
if ( count = = lrNode . Max ) {
newRPosInfo . curpos = followpos [ cPos ] ; //since max has been reached, Get followposition of range node
newRPosInfo . rangeCounters [ lrNode . Pos ] = 0 ; //reset counter
runningPositions [ matchCount ] = newRPosInfo ;
j = matchCount + + ;
}
else if ( count < lrNode . Min ) {
newRPosInfo . curpos = lrNode . NextIteration ;
runningPositions [ matchCount ] = newRPosInfo ;
matchCount + + ;
break ;
}
else { // min <= count < max
newRPosInfo . curpos = lrNode . NextIteration ; //set currentpos to firstpos of node which has the range
runningPositions [ matchCount ] = newRPosInfo ;
j = matchCount + 1 ;
newRPosInfo = runningPositions [ j ] ;
if ( newRPosInfo . rangeCounters = = null ) {
newRPosInfo . rangeCounters = new decimal [ minMaxNodesCount ] ;
}
Array . Copy ( rposInfo . rangeCounters , 0 , newRPosInfo . rangeCounters , 0 , rposInfo . rangeCounters . Length ) ;
newRPosInfo . curpos = followpos [ cPos ] ;
newRPosInfo . rangeCounters [ lrNode . Pos ] = 0 ;
runningPositions [ j ] = newRPosInfo ;
matchCount + = 2 ;
}
currentRunningPosition = runningPositions [ j ] . curpos ;
hasSeenFinalPosition = hasSeenFinalPosition | | currentRunningPosition . Get ( endMarkerPos ) ;
}
}
context . HasMatched = hasSeenFinalPosition ;
context . CurrentState . NumberOfRunningPos = matchCount ;
return positions [ pos ] . particle ;
} //matchcount > 0
errorCode = - 1 ;
context . NeedValidateChildren = false ;
return null ;
}
#if RANGE_DEBUG
private void WriteRunningPositions ( string text , List < RangePositionInfo > runningPositions , XmlQualifiedName name , int length ) {
int counter = 0 ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "" ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "" ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , text + name . Name ) ;
while ( counter < length ) {
BitSet curpos = runningPositions [ counter ] . curpos ;
SequenceNode . WriteBitSet ( curpos ) ;
for ( int rcnt = 0 ; rcnt < runningPositions [ counter ] . rangeCounters . Length ; rcnt + + ) {
Debug . WriteIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "RangeCounter[" + rcnt + "]" + runningPositions [ counter ] . rangeCounters [ rcnt ] + " " ) ;
}
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "" ) ;
Debug . WriteLineIf ( DiagnosticsSwitches . XmlSchemaContentModel . Enabled , "" ) ;
counter + + ;
}
}
#endif
public override bool CompleteValidation ( ValidationState context ) {
return context . HasMatched ;
}
public override ArrayList ExpectedElements ( ValidationState context , bool isRequiredOnly ) {
ArrayList names = null ;
BitSet expectedPos ;
if ( context . RunningPositions ! = null ) {
List < RangePositionInfo > runningPositions = context . RunningPositions ;
expectedPos = new BitSet ( positions . Count ) ;
for ( int i = context . CurrentState . NumberOfRunningPos - 1 ; i > = 0 ; i - - ) {
Debug . Assert ( runningPositions [ i ] . curpos ! = null ) ;
expectedPos . Or ( runningPositions [ i ] . curpos ) ;
}
for ( int pos = expectedPos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = expectedPos . NextSet ( pos ) ) {
if ( names = = null ) {
names = new ArrayList ( ) ;
}
int symbol = positions [ pos ] . symbol ;
if ( symbol > = 0 ) { //non range nodes
XmlSchemaParticle p = positions [ pos ] . particle as XmlSchemaParticle ;
if ( p = = null ) {
string s = symbols . NameOf ( positions [ pos ] . symbol ) ;
if ( s . Length ! = 0 ) {
names . Add ( s ) ;
}
}
else {
string s = p . NameString ;
if ( ! names . Contains ( s ) ) {
names . Add ( s ) ;
}
}
}
}
}
return names ;
}
public override ArrayList ExpectedParticles ( ValidationState context , bool isRequiredOnly , XmlSchemaSet schemaSet ) {
ArrayList particles = new ArrayList ( ) ;
BitSet expectedPos ;
if ( context . RunningPositions ! = null ) {
List < RangePositionInfo > runningPositions = context . RunningPositions ;
expectedPos = new BitSet ( positions . Count ) ;
for ( int i = context . CurrentState . NumberOfRunningPos - 1 ; i > = 0 ; i - - ) {
Debug . Assert ( runningPositions [ i ] . curpos ! = null ) ;
expectedPos . Or ( runningPositions [ i ] . curpos ) ;
}
for ( int pos = expectedPos . NextSet ( - 1 ) ; pos ! = - 1 ; pos = expectedPos . NextSet ( pos ) ) {
int symbol = positions [ pos ] . symbol ;
if ( symbol > = 0 ) { //non range nodes
XmlSchemaParticle p = positions [ pos ] . particle as XmlSchemaParticle ;
if ( p = = null ) {
continue ;
}
AddParticleToExpected ( p , schemaSet , particles ) ;
}
}
}
return particles ;
}
}
sealed class AllElementsContentValidator : ContentValidator {
Hashtable elements ; // unique terminal names to positions in Bitset mapping
object [ ] particles ;
BitSet isRequired ; // required flags
int countRequired = 0 ;
public AllElementsContentValidator ( XmlSchemaContentType contentType , int size , bool isEmptiable ) : base ( contentType , false , isEmptiable ) {
elements = new Hashtable ( size ) ;
particles = new object [ size ] ;
isRequired = new BitSet ( size ) ;
}
public bool AddElement ( XmlQualifiedName name , object particle , bool isEmptiable ) {
if ( elements [ name ] ! = null ) {
return false ;
}
int i = elements . Count ;
elements . Add ( name , i ) ;
particles [ i ] = particle ;
if ( ! isEmptiable ) {
isRequired . Set ( i ) ;
countRequired + + ;
}
return true ;
}
public override bool IsEmptiable {
get { return base . IsEmptiable | | countRequired = = 0 ; }
}
public override void InitValidation ( ValidationState context ) {
Debug . Assert ( elements . Count > 0 ) ;
context . AllElementsSet = new BitSet ( elements . Count ) ; //
context . CurrentState . AllElementsRequired = - 1 ; // no elements at all
}
public override object ValidateElement ( XmlQualifiedName name , ValidationState context , out int errorCode ) {
object lookup = elements [ name ] ;
errorCode = 0 ;
if ( lookup = = null ) {
context . NeedValidateChildren = false ;
return null ;
}
int index = ( int ) lookup ;
if ( context . AllElementsSet [ index ] ) {
errorCode = - 2 ;
return null ;
}
if ( context . CurrentState . AllElementsRequired = = - 1 ) {
context . CurrentState . AllElementsRequired = 0 ;
}
context . AllElementsSet . Set ( index ) ;
if ( isRequired [ index ] ) {
context . CurrentState . AllElementsRequired + + ;
}
return particles [ index ] ;
}
public override bool CompleteValidation ( ValidationState context ) {
if ( context . CurrentState . AllElementsRequired = = countRequired | | IsEmptiable & & context . CurrentState . AllElementsRequired = = - 1 ) {
return true ;
}
return false ;
}
public override ArrayList ExpectedElements ( ValidationState context , bool isRequiredOnly ) {
ArrayList names = null ;
foreach ( DictionaryEntry entry in elements ) {
if ( ! context . AllElementsSet [ ( int ) entry . Value ] & & ( ! isRequiredOnly | | isRequired [ ( int ) entry . Value ] ) ) {
if ( names = = null ) {
names = new ArrayList ( ) ;
}
names . Add ( entry . Key ) ;
}
}
return names ;
}
public override ArrayList ExpectedParticles ( ValidationState context , bool isRequiredOnly , XmlSchemaSet schemaSet ) {
ArrayList expectedParticles = new ArrayList ( ) ;
foreach ( DictionaryEntry entry in elements ) {
if ( ! context . AllElementsSet [ ( int ) entry . Value ] & & ( ! isRequiredOnly | | isRequired [ ( int ) entry . Value ] ) ) {
AddParticleToExpected ( this . particles [ ( int ) entry . Value ] as XmlSchemaParticle , schemaSet , expectedParticles ) ;
}
}
return expectedParticles ;
}
}
#endregion
}