2008 lines
80 KiB
C#
2008 lines
80 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="ContentValidator.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
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
|
||
|
}
|