536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1369 lines
61 KiB
C#
1369 lines
61 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="XmlSchemaSet.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
//------------------------------------------------------------------------------
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
using System.Threading;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace System.Xml.Schema {
|
|
#if SILVERLIGHT
|
|
public class XmlSchemaSet
|
|
{
|
|
//Empty XmlSchemaSet class to enable backward compatibility of XmlSchemaProvideAttribute
|
|
//Add private ctor to prevent constructing of this class
|
|
XmlSchemaSet() { }
|
|
}
|
|
#else
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet"]/*' />
|
|
/// <devdoc>
|
|
/// <para>The XmlSchemaSet contains a set of namespace URI's.
|
|
/// Each namespace also have an associated private data cache
|
|
/// corresponding to the XML-Data Schema or W3C XML Schema.
|
|
/// The XmlSchemaSet will able to load only XSD schemas,
|
|
/// and compile them into an internal "cooked schema representation".
|
|
/// The Validate method then uses this internal representation for
|
|
/// efficient runtime validation of any given subtree.</para>
|
|
/// </devdoc>
|
|
public class XmlSchemaSet {
|
|
XmlNameTable nameTable;
|
|
SchemaNames schemaNames;
|
|
SortedList schemas; // List of source schemas
|
|
|
|
//Event handling
|
|
ValidationEventHandler internalEventHandler;
|
|
ValidationEventHandler eventHandler;
|
|
|
|
bool isCompiled = false;
|
|
|
|
//Dictionary<Uri, XmlSchema> schemaLocations;
|
|
//Dictionary<ChameleonKey, XmlSchema> chameleonSchemas;
|
|
Hashtable schemaLocations;
|
|
Hashtable chameleonSchemas;
|
|
|
|
Hashtable targetNamespaces;
|
|
bool compileAll;
|
|
|
|
//Cached Compiled Info
|
|
SchemaInfo cachedCompiledInfo;
|
|
|
|
//Reader settings to parse schema
|
|
XmlReaderSettings readerSettings;
|
|
XmlSchema schemaForSchema; //Only one schema for schema per set
|
|
|
|
//Schema compilation settings
|
|
XmlSchemaCompilationSettings compilationSettings;
|
|
|
|
internal XmlSchemaObjectTable elements;
|
|
internal XmlSchemaObjectTable attributes;
|
|
internal XmlSchemaObjectTable schemaTypes;
|
|
internal XmlSchemaObjectTable substitutionGroups;
|
|
private XmlSchemaObjectTable typeExtensions;
|
|
|
|
//Thread safety
|
|
private Object internalSyncObject;
|
|
internal Object InternalSyncObject {
|
|
get {
|
|
if (internalSyncObject == null) {
|
|
Object o = new Object();
|
|
Interlocked.CompareExchange<Object>(ref internalSyncObject, o, null);
|
|
}
|
|
return internalSyncObject;
|
|
}
|
|
}
|
|
|
|
//Constructors
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.XmlSchemaSet"]/*' />
|
|
/// <devdoc>
|
|
/// <para>Construct a new empty schema schemas.</para>
|
|
/// </devdoc>
|
|
public XmlSchemaSet() : this(new NameTable()) {
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.XmlSchemaSet1"]/*' />
|
|
/// <devdoc>
|
|
/// <para>Construct a new empty schema schemas with associated XmlNameTable.
|
|
/// The XmlNameTable is used when loading schemas</para>
|
|
/// </devdoc>
|
|
public XmlSchemaSet(XmlNameTable nameTable) {
|
|
if (nameTable == null) {
|
|
throw new ArgumentNullException("nameTable");
|
|
}
|
|
this.nameTable = nameTable;
|
|
schemas = new SortedList();
|
|
|
|
/*schemaLocations = new Dictionary<Uri, XmlSchema>();
|
|
chameleonSchemas = new Dictionary<ChameleonKey, XmlSchema>();*/
|
|
schemaLocations = new Hashtable();
|
|
chameleonSchemas = new Hashtable();
|
|
targetNamespaces = new Hashtable();
|
|
internalEventHandler = new ValidationEventHandler(InternalValidationCallback);
|
|
eventHandler = internalEventHandler;
|
|
|
|
readerSettings = new XmlReaderSettings();
|
|
|
|
// we don't have to check XmlReaderSettings.EnableLegacyXmlSettings() here because the following
|
|
// code will return same result either we are running on v4.5 or later
|
|
if (readerSettings.GetXmlResolver() == null)
|
|
{
|
|
// The created resolver will be used in the schema validation only
|
|
readerSettings.XmlResolver = new XmlUrlResolver();
|
|
readerSettings.IsXmlResolverSet = false;
|
|
}
|
|
|
|
readerSettings.NameTable = nameTable;
|
|
readerSettings.DtdProcessing = DtdProcessing.Prohibit;
|
|
|
|
compilationSettings = new XmlSchemaCompilationSettings();
|
|
cachedCompiledInfo = new SchemaInfo();
|
|
compileAll = true;
|
|
}
|
|
|
|
|
|
//Public Properties
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.NameTable"]/*' />
|
|
/// <devdoc>
|
|
/// <para>The default XmlNameTable used by the XmlSchemaSet when loading new schemas.</para>
|
|
/// </devdoc>
|
|
public XmlNameTable NameTable {
|
|
get { return nameTable;}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.ValidationEventHandler"]/*' />
|
|
public event ValidationEventHandler ValidationEventHandler {
|
|
add {
|
|
eventHandler -= internalEventHandler;
|
|
eventHandler += value;
|
|
if (eventHandler == null) {
|
|
eventHandler = internalEventHandler;
|
|
}
|
|
}
|
|
remove {
|
|
eventHandler -= value;
|
|
if (eventHandler == null) {
|
|
eventHandler = internalEventHandler;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.IsCompiled"]/*' />
|
|
/// <devdoc>
|
|
/// <para>IsCompiled is true when the schema set is in compiled state</para>
|
|
/// </devdoc>
|
|
public bool IsCompiled {
|
|
get {
|
|
return isCompiled;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.XmlResolver"]/*' />
|
|
/// <devdoc>
|
|
/// <para></para>
|
|
/// </devdoc>
|
|
public XmlResolver XmlResolver {
|
|
set {
|
|
readerSettings.XmlResolver = value;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.CompilationSettings"]/*' />
|
|
/// <devdoc>
|
|
/// <para></para>
|
|
/// </devdoc>
|
|
public XmlSchemaCompilationSettings CompilationSettings {
|
|
get {
|
|
return compilationSettings;
|
|
}
|
|
set {
|
|
compilationSettings = value;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Count"]/*' />
|
|
/// <devdoc>
|
|
/// <para>Returns the count of schemas in the set</para>
|
|
/// </devdoc>
|
|
public int Count {
|
|
get {
|
|
return schemas.Count;
|
|
}
|
|
}
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.GlobalElements"]/*' />
|
|
/// <devdoc>
|
|
/// <para></para>
|
|
/// </devdoc>
|
|
public XmlSchemaObjectTable GlobalElements {
|
|
get {
|
|
if (elements == null) {
|
|
elements = new XmlSchemaObjectTable();
|
|
}
|
|
return elements;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.GlobalAttributes"]/*' />
|
|
/// <devdoc>
|
|
/// <para></para>
|
|
/// </devdoc>
|
|
public XmlSchemaObjectTable GlobalAttributes {
|
|
get {
|
|
if (attributes == null) {
|
|
attributes = new XmlSchemaObjectTable();
|
|
}
|
|
return attributes;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.GlobalTypes"]/*' />
|
|
/// <devdoc>
|
|
/// <para></para>
|
|
/// </devdoc>
|
|
public XmlSchemaObjectTable GlobalTypes {
|
|
get {
|
|
if (schemaTypes == null) {
|
|
schemaTypes = new XmlSchemaObjectTable();
|
|
}
|
|
return schemaTypes;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.SubstitutionGroups"]/*' />
|
|
/// <devdoc>
|
|
/// <para></para>
|
|
/// </devdoc>
|
|
///
|
|
internal XmlSchemaObjectTable SubstitutionGroups {
|
|
get {
|
|
if (substitutionGroups == null) {
|
|
substitutionGroups = new XmlSchemaObjectTable();
|
|
}
|
|
return substitutionGroups;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Table of all types extensions
|
|
/// </summary>
|
|
internal Hashtable SchemaLocations {
|
|
get {
|
|
return schemaLocations;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Table of all types extensions
|
|
/// </summary>
|
|
internal XmlSchemaObjectTable TypeExtensions {
|
|
get {
|
|
if (typeExtensions == null) {
|
|
typeExtensions = new XmlSchemaObjectTable();
|
|
}
|
|
return typeExtensions;
|
|
}
|
|
}
|
|
//Public Methods
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add1"]/*' />
|
|
/// <devdoc>
|
|
/// <para>Add the schema located by the given URL into the schema schemas.
|
|
/// If the given schema references other namespaces, the schemas for those other
|
|
/// namespaces are NOT automatically loaded.</para>
|
|
/// </devdoc>
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
public XmlSchema Add(String targetNamespace, String schemaUri) {
|
|
if (schemaUri == null || schemaUri.Length == 0) {
|
|
throw new ArgumentNullException("schemaUri");
|
|
}
|
|
if (targetNamespace != null) {
|
|
targetNamespace = XmlComplianceUtil.CDataNormalize(targetNamespace);
|
|
}
|
|
XmlSchema schema = null;
|
|
lock (InternalSyncObject) {
|
|
//Check if schema from url has already been added
|
|
XmlResolver tempResolver = readerSettings.GetXmlResolver();
|
|
if ( tempResolver == null ) {
|
|
tempResolver = new XmlUrlResolver();
|
|
}
|
|
Uri tempSchemaUri = tempResolver.ResolveUri(null, schemaUri);
|
|
if (IsSchemaLoaded(tempSchemaUri, targetNamespace, out schema)) {
|
|
return schema;
|
|
}
|
|
else {
|
|
//Url already not processed; Load SOM from url
|
|
XmlReader reader = XmlReader.Create(schemaUri, readerSettings);
|
|
try {
|
|
schema = Add(targetNamespace, ParseSchema(targetNamespace, reader)); //
|
|
while(reader.Read());// wellformness check;
|
|
}
|
|
finally {
|
|
reader.Close();
|
|
}
|
|
}
|
|
}
|
|
return schema;
|
|
}
|
|
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add4"]/*' />
|
|
/// <devdoc>
|
|
/// <para>Add the given schema into the schema schemas.
|
|
/// If the given schema references other namespaces, the schemas for those
|
|
/// other namespaces are NOT automatically loaded.</para>
|
|
/// </devdoc>
|
|
public XmlSchema Add(String targetNamespace, XmlReader schemaDocument) {
|
|
if (schemaDocument == null) {
|
|
throw new ArgumentNullException("schemaDocument");
|
|
}
|
|
if (targetNamespace != null) {
|
|
targetNamespace = XmlComplianceUtil.CDataNormalize(targetNamespace);
|
|
}
|
|
lock (InternalSyncObject) {
|
|
XmlSchema schema = null;
|
|
Uri schemaUri = new Uri(schemaDocument.BaseURI, UriKind.RelativeOrAbsolute);
|
|
if (IsSchemaLoaded(schemaUri, targetNamespace, out schema)) {
|
|
return schema;
|
|
}
|
|
else {
|
|
DtdProcessing dtdProcessing = this.readerSettings.DtdProcessing;
|
|
SetDtdProcessing(schemaDocument);
|
|
schema = Add(targetNamespace, ParseSchema(targetNamespace, schemaDocument));
|
|
this.readerSettings.DtdProcessing = dtdProcessing; //reset dtdProcessing setting
|
|
return schema;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add5"]/*' />
|
|
/// <devdoc>
|
|
/// <para>Adds all the namespaces defined in the given schemas
|
|
/// (including their associated schemas) to this schemas.</para>
|
|
/// </devdoc>
|
|
public void Add(XmlSchemaSet schemas) {
|
|
if (schemas == null) {
|
|
throw new ArgumentNullException("schemas");
|
|
}
|
|
if (this == schemas) {
|
|
return;
|
|
}
|
|
bool thisLockObtained = false;
|
|
bool schemasLockObtained = false;
|
|
try {
|
|
while(true) {
|
|
Monitor.TryEnter(InternalSyncObject, ref thisLockObtained);
|
|
if (thisLockObtained) {
|
|
Monitor.TryEnter(schemas.InternalSyncObject, ref schemasLockObtained);
|
|
if (schemasLockObtained) {
|
|
break;
|
|
}
|
|
else {
|
|
Monitor.Exit(InternalSyncObject); //Give up this lock and try both again
|
|
thisLockObtained = false;
|
|
Thread.Yield(); //Let the thread that holds the lock run
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
XmlSchema currentSchema;
|
|
//
|
|
if (schemas.IsCompiled) {
|
|
CopyFromCompiledSet(schemas);
|
|
}
|
|
else {
|
|
bool remove = false;
|
|
string tns = null;
|
|
foreach(XmlSchema schema in schemas.SortedSchemas.Values) {
|
|
tns = schema.TargetNamespace;
|
|
if (tns == null) {
|
|
tns = string.Empty;
|
|
}
|
|
if (this.schemas.ContainsKey(schema.SchemaId) || FindSchemaByNSAndUrl(schema.BaseUri, tns, null) != null) { //Do not already existing url
|
|
continue;
|
|
}
|
|
currentSchema = Add(schema.TargetNamespace, schema);
|
|
if(currentSchema == null) {
|
|
remove = true;
|
|
break;
|
|
}
|
|
}
|
|
//Remove all from the set if even one schema in the passed in set is not preprocessed.
|
|
if (remove) {
|
|
foreach(XmlSchema schema in schemas.SortedSchemas.Values) { //Remove all previously added schemas from the set
|
|
this.schemas.Remove(schema.SchemaId); //Might remove schema that was already there and was not added thru this operation
|
|
schemaLocations.Remove(schema.BaseUri);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally { //release locks on sets
|
|
if (thisLockObtained) {
|
|
Monitor.Exit(InternalSyncObject);
|
|
}
|
|
if (schemasLockObtained) {
|
|
Monitor.Exit(schemas.InternalSyncObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add6"]/*' />
|
|
public XmlSchema Add(XmlSchema schema) {
|
|
if (schema == null) {
|
|
throw new ArgumentNullException("schema");
|
|
}
|
|
lock (InternalSyncObject) {
|
|
if (schemas.ContainsKey(schema.SchemaId)) {
|
|
return schema;
|
|
}
|
|
return Add(schema.TargetNamespace, schema);
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Remove"]/*' />
|
|
public XmlSchema Remove(XmlSchema schema) {
|
|
return Remove(schema, true);
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.RemoveRecursive"]/*' />
|
|
public bool RemoveRecursive(XmlSchema schemaToRemove) {
|
|
|
|
if (schemaToRemove == null) {
|
|
throw new ArgumentNullException("schemaToRemove");
|
|
}
|
|
if (!schemas.ContainsKey(schemaToRemove.SchemaId)) {
|
|
return false;
|
|
}
|
|
|
|
lock (InternalSyncObject) { //Need to lock here so that remove cannot be called while the set is being compiled
|
|
if (schemas.ContainsKey(schemaToRemove.SchemaId)) { //Need to check again
|
|
|
|
//Build disallowedNamespaces list
|
|
Hashtable disallowedNamespaces = new Hashtable();
|
|
disallowedNamespaces.Add(GetTargetNamespace(schemaToRemove), schemaToRemove);
|
|
string importedNS;
|
|
for (int i = 0; i < schemaToRemove.ImportedNamespaces.Count; i++) {
|
|
importedNS = (string)schemaToRemove.ImportedNamespaces[i];
|
|
if (disallowedNamespaces[importedNS] == null) {
|
|
disallowedNamespaces.Add(importedNS, importedNS);
|
|
}
|
|
}
|
|
|
|
//Removal list is all schemas imported by this schema directly or indirectly
|
|
//Need to check if other schemas in the set import schemaToRemove / any of its imports
|
|
ArrayList needToCheckSchemaList = new ArrayList();
|
|
XmlSchema mainSchema;
|
|
for (int i =0; i < schemas.Count; i++) {
|
|
mainSchema = (XmlSchema)schemas.GetByIndex(i);
|
|
if (mainSchema == schemaToRemove ||
|
|
schemaToRemove.ImportedSchemas.Contains(mainSchema)) {
|
|
continue;
|
|
}
|
|
needToCheckSchemaList.Add(mainSchema);
|
|
}
|
|
|
|
mainSchema = null;
|
|
for (int i = 0; i < needToCheckSchemaList.Count; i++) { //Perf: Not using nested foreach here
|
|
mainSchema = (XmlSchema)needToCheckSchemaList[i];
|
|
|
|
if (mainSchema.ImportedNamespaces.Count > 0) {
|
|
foreach(string tns in disallowedNamespaces.Keys) {
|
|
if (mainSchema.ImportedNamespaces.Contains(tns)) {
|
|
SendValidationEvent(new XmlSchemaException(Res.Sch_SchemaNotRemoved, string.Empty), XmlSeverityType.Warning);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Remove(schemaToRemove, true);
|
|
for (int i = 0; i < schemaToRemove.ImportedSchemas.Count; ++i) {
|
|
XmlSchema impSchema = (XmlSchema)schemaToRemove.ImportedSchemas[i];
|
|
Remove(impSchema, true);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Contains1"]/*' />
|
|
public bool Contains(String targetNamespace) {
|
|
if (targetNamespace == null) {
|
|
targetNamespace = string.Empty;
|
|
}
|
|
return targetNamespaces[targetNamespace] != null;
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Contains2"]/*' />
|
|
public bool Contains(XmlSchema schema) {
|
|
if (schema == null) {
|
|
throw new ArgumentNullException("schema");
|
|
}
|
|
return schemas.ContainsValue(schema);
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Compile"]/*' />
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public void Compile() {
|
|
if (isCompiled) {
|
|
return;
|
|
}
|
|
if (schemas.Count == 0) {
|
|
ClearTables(); //Clear any previously present compiled state left by calling just Remove() on the set
|
|
cachedCompiledInfo = new SchemaInfo();
|
|
isCompiled = true;
|
|
compileAll = false;
|
|
return;
|
|
}
|
|
lock (InternalSyncObject) {
|
|
|
|
if (!isCompiled) { //Locking before checking isCompiled to avoid problems with double locking
|
|
Compiler compiler = new Compiler(nameTable, eventHandler, schemaForSchema, compilationSettings);
|
|
SchemaInfo newCompiledInfo = new SchemaInfo();
|
|
int schemaIndex = 0;
|
|
if (!compileAll) { //if we are not compiling everything again, Move the pre-compiled schemas to the compiler's tables
|
|
compiler.ImportAllCompiledSchemas(this);
|
|
}
|
|
try { //First thing to do in the try block is to acquire locks since finally will try to release them.
|
|
//If we dont accuire the locks first, and an exception occurs in the code before the locking code, then Threading.SynchronizationLockException will be thrown
|
|
//when attempting to release it in the finally block
|
|
XmlSchema currentSchema;
|
|
XmlSchema xmlNSSchema = Preprocessor.GetBuildInSchema();
|
|
for (schemaIndex = 0; schemaIndex < schemas.Count; schemaIndex++) {
|
|
currentSchema = (XmlSchema)schemas.GetByIndex(schemaIndex);
|
|
|
|
//Lock schema to be compiled
|
|
#pragma warning disable 0618
|
|
//@
|
|
Monitor.Enter(currentSchema);
|
|
#pragma warning restore 0618
|
|
if (!currentSchema.IsPreprocessed)
|
|
{
|
|
SendValidationEvent(new XmlSchemaException(Res.Sch_SchemaNotPreprocessed, string.Empty), XmlSeverityType.Error);
|
|
isCompiled = false;
|
|
return;
|
|
}
|
|
if (currentSchema.IsCompiledBySet) {
|
|
if (!compileAll) {
|
|
continue;
|
|
}
|
|
else if ((object)currentSchema == (object)xmlNSSchema) { // prepare for xml namespace schema without cleanup
|
|
compiler.Prepare(currentSchema, false);
|
|
continue;
|
|
}
|
|
}
|
|
compiler.Prepare(currentSchema, true);
|
|
}
|
|
|
|
isCompiled = compiler.Execute(this, newCompiledInfo);
|
|
if (isCompiled) {
|
|
if (!compileAll) {
|
|
newCompiledInfo.Add(cachedCompiledInfo, eventHandler); //Add all the items from the old to the new compiled object
|
|
}
|
|
compileAll = false;
|
|
cachedCompiledInfo = newCompiledInfo; //Replace the compiled info in the set after successful compilation
|
|
}
|
|
}
|
|
finally {
|
|
//Release locks on all schemas
|
|
XmlSchema currentSchema;
|
|
if (schemaIndex == schemas.Count) {
|
|
schemaIndex--;
|
|
}
|
|
for (int i = schemaIndex; i >= 0; i--) {
|
|
currentSchema = (XmlSchema)schemas.GetByIndex(i);
|
|
if (currentSchema == Preprocessor.GetBuildInSchema()) { //dont re-set compiled flags for xml namespace schema
|
|
Monitor.Exit(currentSchema);
|
|
continue;
|
|
}
|
|
currentSchema.IsCompiledBySet = isCompiled;
|
|
Monitor.Exit(currentSchema);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Reprocess"]/*' />
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public XmlSchema Reprocess(XmlSchema schema) {
|
|
// Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Remove, Add and AddSchemaToSet
|
|
// methods. If you change anything here *make sure* to update Remove/Add/AddSchemaToSet method(s) accordingly.
|
|
// The only difference is that we don't touch .schemas collection here to not break a code like this:
|
|
// foreach(XmlSchema s in schemaset.schemas) { schemaset.Reprocess(s); }
|
|
// This is by purpose.
|
|
if (schema == null) {
|
|
throw new ArgumentNullException("schema");
|
|
}
|
|
if (!schemas.ContainsKey(schema.SchemaId)) {
|
|
throw new ArgumentException(Res.GetString(Res.Sch_SchemaDoesNotExist), "schema");
|
|
}
|
|
XmlSchema originalSchema = schema;
|
|
lock (InternalSyncObject) { //Lock set so that set cannot be compiled in another thread
|
|
|
|
// This code is copied from method:
|
|
// Remove(XmlSchema schema, bool forceCompile)
|
|
// If you changed anything here go and change the same in Remove(XmlSchema schema, bool forceCompile) method
|
|
#region Copied from Remove(XmlSchema schema, bool forceCompile)
|
|
|
|
RemoveSchemaFromGlobalTables(schema);
|
|
RemoveSchemaFromCaches(schema);
|
|
if (schema.BaseUri != null) {
|
|
schemaLocations.Remove(schema.BaseUri);
|
|
}
|
|
string tns = GetTargetNamespace(schema);
|
|
if (Schemas(tns).Count == 0) { //This is the only schema for that namespace
|
|
targetNamespaces.Remove(tns);
|
|
}
|
|
isCompiled = false;
|
|
compileAll = true; //Force compilation of the whole set; This is when the set is not completely thread-safe
|
|
|
|
#endregion //Copied from Remove(XmlSchema schema, bool forceCompile)
|
|
|
|
|
|
// This code is copied from method:
|
|
// Add(string targetNamespace, XmlSchema schema)
|
|
// If you changed anything here go and change the same in Add(string targetNamespace, XmlSchema schema) method
|
|
#region Copied from Add(string targetNamespace, XmlSchema schema)
|
|
|
|
if (schema.ErrorCount != 0) { //Schema with parsing errors cannot be loaded
|
|
return originalSchema;
|
|
}
|
|
if (PreprocessSchema(ref schema, schema.TargetNamespace)) { //No perf opt for already compiled schemas
|
|
|
|
// This code is copied from method:
|
|
// AddSchemaToSet(XmlSchema schema)
|
|
// If you changed anything here go and change the same in AddSchemaToSet(XmlSchema schema) method
|
|
#region Copied from AddSchemaToSet(XmlSchema schema)
|
|
|
|
//Add to targetNamespaces table
|
|
if (targetNamespaces[tns] == null) {
|
|
targetNamespaces.Add(tns, tns);
|
|
}
|
|
if (schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null) { //it has xs:anyType
|
|
schemaForSchema = schema;
|
|
}
|
|
for (int i = 0; i < schema.ImportedSchemas.Count; ++i) { //Once preprocessed external schemas property is set
|
|
XmlSchema s = (XmlSchema)schema.ImportedSchemas[i];
|
|
if (!schemas.ContainsKey(s.SchemaId)) {
|
|
schemas.Add(s.SchemaId, s);
|
|
}
|
|
tns = GetTargetNamespace(s);
|
|
if (targetNamespaces[tns] == null) {
|
|
targetNamespaces.Add(tns, tns);
|
|
}
|
|
if (schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null) { //it has xs:anyType
|
|
schemaForSchema = schema;
|
|
}
|
|
}
|
|
#endregion //Copied from AddSchemaToSet(XmlSchema schema)
|
|
return schema;
|
|
}
|
|
#endregion // Copied from Add(string targetNamespace, XmlSchema schema)
|
|
|
|
return originalSchema;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.CopyTo"]/*' />
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public void CopyTo(XmlSchema[] schemas, int index) {
|
|
if (schemas == null)
|
|
throw new ArgumentNullException("schemas");
|
|
if (index < 0 || index > schemas.Length -1 )
|
|
throw new ArgumentOutOfRangeException("index");
|
|
this.schemas.Values.CopyTo(schemas, index);
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Schemas1"]/*' />
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public ICollection Schemas() {
|
|
return schemas.Values;
|
|
}
|
|
|
|
/// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Schemas1"]/*' />
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public ICollection Schemas(String targetNamespace) {
|
|
ArrayList tnsSchemas = new ArrayList();
|
|
XmlSchema currentSchema;
|
|
if (targetNamespace == null) {
|
|
targetNamespace = string.Empty;
|
|
}
|
|
for (int i=0; i < schemas.Count; i++) {
|
|
currentSchema = (XmlSchema)schemas.GetByIndex(i);
|
|
if (GetTargetNamespace(currentSchema) == targetNamespace) {
|
|
tnsSchemas.Add(currentSchema);
|
|
}
|
|
}
|
|
return tnsSchemas;
|
|
}
|
|
|
|
//Internal Methods
|
|
|
|
private XmlSchema Add(string targetNamespace, XmlSchema schema) {
|
|
// Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Reprocess
|
|
// method. If you change anything here *make sure* to update Reprocess method accordingly.
|
|
|
|
if (schema == null || schema.ErrorCount != 0) { //Schema with parsing errors cannot be loaded
|
|
return null;
|
|
}
|
|
|
|
// This code is copied to method:
|
|
// Reprocess(XmlSchema schema)
|
|
// If you changed anything here go and change the same in Reprocess(XmlSchema schema) method
|
|
if (PreprocessSchema(ref schema, targetNamespace)) { //No perf opt for already compiled schemas
|
|
AddSchemaToSet(schema);
|
|
isCompiled = false;
|
|
return schema;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
#if TRUST_COMPILE_STATE
|
|
private void AddCompiledSchema(XmlSchema schema) {
|
|
if (schema.IsCompiledBySet ) { //trust compiled state always if it is not a chameleon schema
|
|
VerifyTables();
|
|
SchemaInfo newCompiledInfo = new SchemaInfo();
|
|
XmlSchemaObjectTable substitutionGroupsTable = null;
|
|
if (!AddToCompiledInfo(schema, newCompiledInfo, ref substitutionGroupsTable)) { //Error while adding main schema
|
|
return null;
|
|
}
|
|
foreach (XmlSchema impSchema in schema.ImportedSchemas) {
|
|
if (!AddToCompiledInfo(impSchema, newCompiledInfo, ref substitutionGroupsTable)) { //Error while adding imports
|
|
return null;
|
|
}
|
|
}
|
|
newCompiledInfo.Add(cachedCompiledInfo, eventHandler); //Add existing compiled info
|
|
cachedCompiledInfo = newCompiledInfo;
|
|
if (substitutionGroupsTable != null) {
|
|
ProcessNewSubstitutionGroups(substitutionGroupsTable, true);
|
|
}
|
|
if (schemas.Count == 0) { //If its the first compiled schema being added, then set doesnt need to be compiled
|
|
isCompiled = true;
|
|
compileAll = false;
|
|
}
|
|
AddSchemaToSet(schema);
|
|
return schema;
|
|
}
|
|
}
|
|
|
|
private bool AddToCompiledInfo(XmlSchema schema, SchemaInfo newCompiledInfo, ref XmlSchemaObjectTable substTable) {
|
|
//Add schema's compiled tables to the set
|
|
if (schema.BaseUri != null && schemaLocations[schema.BaseUri] == null) { //Update schemaLocations table
|
|
schemaLocations.Add(schema.BaseUri, schema);
|
|
}
|
|
|
|
foreach (XmlSchemaElement element in schema.Elements.Values) {
|
|
if(!AddToTable(elements, element.QualifiedName, element)) {
|
|
RemoveSchemaFromGlobalTables(schema);
|
|
return false;
|
|
}
|
|
XmlQualifiedName head = element.SubstitutionGroup;
|
|
if (!head.IsEmpty) {
|
|
if (substTable == null) {
|
|
substTable = new XmlSchemaObjectTable();
|
|
}
|
|
XmlSchemaSubstitutionGroup substitutionGroup = (XmlSchemaSubstitutionGroup)substTable[head];
|
|
if (substitutionGroup == null) {
|
|
substitutionGroup = new XmlSchemaSubstitutionGroup();
|
|
substitutionGroup.Examplar = head;
|
|
substTable.Add(head, substitutionGroup);
|
|
}
|
|
ArrayList members = substitutionGroup.Members;
|
|
if (!members.Contains(element)) { //Members might contain element if the same schema is included and imported through different paths. Imp, hence will be added to set directly
|
|
members.Add(element);
|
|
}
|
|
}
|
|
}
|
|
foreach (XmlSchemaAttribute attribute in schema.Attributes.Values) {
|
|
if (!AddToTable(attributes, attribute.QualifiedName, attribute)) {
|
|
RemoveSchemaFromGlobalTables(schema);
|
|
return false;
|
|
}
|
|
}
|
|
foreach (XmlSchemaType schemaType in schema.SchemaTypes.Values) {
|
|
if (!AddToTable(schemaTypes, schemaType.QualifiedName, schemaType)) {
|
|
RemoveSchemaFromGlobalTables(schema);
|
|
return false;
|
|
}
|
|
}
|
|
schema.AddCompiledInfo(newCompiledInfo);
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
//For use by the validator when loading schemaLocations in the instance
|
|
internal void Add(String targetNamespace, XmlReader reader, Hashtable validatedNamespaces) {
|
|
if (reader == null) {
|
|
throw new ArgumentNullException("reader");
|
|
}
|
|
if (targetNamespace == null) {
|
|
targetNamespace = string.Empty;
|
|
}
|
|
if (validatedNamespaces[targetNamespace] != null) {
|
|
if (FindSchemaByNSAndUrl(new Uri(reader.BaseURI, UriKind.RelativeOrAbsolute), targetNamespace, null) != null) {
|
|
return;
|
|
}
|
|
else {
|
|
throw new XmlSchemaException(Res.Sch_ComponentAlreadySeenForNS, targetNamespace);
|
|
}
|
|
}
|
|
|
|
//Not locking set as this will not be accessible outside the validator
|
|
XmlSchema schema;
|
|
if (IsSchemaLoaded(new Uri(reader.BaseURI, UriKind.RelativeOrAbsolute), targetNamespace, out schema)) {
|
|
return;
|
|
}
|
|
else { //top-level schema not present for same url
|
|
schema = ParseSchema(targetNamespace, reader);
|
|
|
|
//Store the previous locations
|
|
DictionaryEntry[] oldLocations = new DictionaryEntry[schemaLocations.Count];
|
|
schemaLocations.CopyTo(oldLocations, 0);
|
|
|
|
//Add to set
|
|
Add(targetNamespace, schema);
|
|
if (schema.ImportedSchemas.Count > 0) { //Check imports
|
|
string tns;
|
|
for (int i = 0; i < schema.ImportedSchemas.Count; ++i) {
|
|
XmlSchema impSchema = (XmlSchema)schema.ImportedSchemas[i];
|
|
tns = impSchema.TargetNamespace;
|
|
if (tns == null) {
|
|
tns = string.Empty;
|
|
}
|
|
if (validatedNamespaces[tns] != null && (FindSchemaByNSAndUrl(impSchema.BaseUri, tns, oldLocations) == null) ) {
|
|
RemoveRecursive(schema);
|
|
throw new XmlSchemaException(Res.Sch_ComponentAlreadySeenForNS, tns);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal XmlSchema FindSchemaByNSAndUrl(Uri schemaUri, string ns, DictionaryEntry[] locationsTable) {
|
|
if (schemaUri == null || schemaUri.OriginalString.Length == 0) {
|
|
return null;
|
|
}
|
|
XmlSchema schema = null;
|
|
if (locationsTable == null) {
|
|
schema = (XmlSchema)schemaLocations[schemaUri];
|
|
}
|
|
else {
|
|
for (int i = 0; i < locationsTable.Length; i++) {
|
|
if (schemaUri.Equals(locationsTable[i].Key)) {
|
|
schema = (XmlSchema)locationsTable[i].Value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (schema != null) {
|
|
Debug.Assert(ns != null);
|
|
string tns = schema.TargetNamespace == null ? string.Empty : schema.TargetNamespace;
|
|
if (tns == ns) {
|
|
return schema;
|
|
}
|
|
else if (tns == string.Empty) { //There could be a chameleon for same ns
|
|
// It is OK to pass in the schema we have found so far, since it must have the schemaUri we're looking for
|
|
// (we found it that way above) and it must be the original chameleon schema (the one without target ns)
|
|
// as we don't add the chameleon copies into the locations tables above.
|
|
Debug.Assert(schema.BaseUri.Equals(schemaUri));
|
|
ChameleonKey cKey = new ChameleonKey(ns, schema);
|
|
schema = (XmlSchema)chameleonSchemas[cKey]; //Need not clone if a schema for that namespace already exists
|
|
}
|
|
else {
|
|
schema = null;
|
|
}
|
|
}
|
|
return schema;
|
|
}
|
|
|
|
private void SetDtdProcessing(XmlReader reader) {
|
|
if (reader.Settings != null) {
|
|
this.readerSettings.DtdProcessing = reader.Settings.DtdProcessing;
|
|
}
|
|
else {
|
|
XmlTextReader v1Reader = reader as XmlTextReader;
|
|
if (v1Reader != null) {
|
|
this.readerSettings.DtdProcessing = v1Reader.DtdProcessing;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddSchemaToSet(XmlSchema schema) {
|
|
// Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Reprocess
|
|
// method. If you change anything here *make sure* to update Reprocess method accordingly.
|
|
|
|
schemas.Add(schema.SchemaId, schema);
|
|
//Add to targetNamespaces table
|
|
|
|
// This code is copied to method:
|
|
// Reprocess(XmlSchema schema)
|
|
// If you changed anything here go and change the same in Reprocess(XmlSchema schema) method
|
|
#region This code is copied to Reprocess(XmlSchema schema) method
|
|
|
|
string tns = GetTargetNamespace(schema);
|
|
if (targetNamespaces[tns] == null) {
|
|
targetNamespaces.Add(tns, tns);
|
|
}
|
|
if (schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null) { //it has xs:anyType
|
|
schemaForSchema = schema;
|
|
}
|
|
for (int i = 0; i < schema.ImportedSchemas.Count; ++i) { //Once preprocessed external schemas property is set
|
|
XmlSchema s = (XmlSchema)schema.ImportedSchemas[i];
|
|
if (!schemas.ContainsKey(s.SchemaId)) {
|
|
schemas.Add(s.SchemaId, s);
|
|
}
|
|
tns = GetTargetNamespace(s);
|
|
if (targetNamespaces[tns] == null) {
|
|
targetNamespaces.Add(tns, tns);
|
|
}
|
|
if (schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null) { //it has xs:anyType
|
|
schemaForSchema = schema;
|
|
}
|
|
}
|
|
|
|
#endregion // This code is copied to Reprocess(XmlSchema schema) method
|
|
}
|
|
|
|
private void ProcessNewSubstitutionGroups(XmlSchemaObjectTable substitutionGroupsTable, bool resolve) {
|
|
foreach(XmlSchemaSubstitutionGroup substGroup in substitutionGroupsTable.Values) {
|
|
if (resolve) { //Resolve substitutionGroups within this schema
|
|
ResolveSubstitutionGroup(substGroup, substitutionGroupsTable);
|
|
}
|
|
|
|
//Add or Merge new substitutionGroups with those that already exist in the set
|
|
XmlQualifiedName head = substGroup.Examplar;
|
|
XmlSchemaSubstitutionGroup oldSubstGroup = (XmlSchemaSubstitutionGroup)substitutionGroups[head];
|
|
if (oldSubstGroup != null) {
|
|
for (int i = 0; i < substGroup.Members.Count; ++i) {
|
|
if (!oldSubstGroup.Members.Contains(substGroup.Members[i])) {
|
|
oldSubstGroup.Members.Add(substGroup.Members[i]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
AddToTable(substitutionGroups, head, substGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ResolveSubstitutionGroup(XmlSchemaSubstitutionGroup substitutionGroup, XmlSchemaObjectTable substTable) {
|
|
List<XmlSchemaElement> newMembers = null;
|
|
XmlSchemaElement headElement = (XmlSchemaElement)elements[substitutionGroup.Examplar];
|
|
if (substitutionGroup.Members.Contains(headElement)) {// already checked
|
|
return;
|
|
}
|
|
for (int i = 0; i < substitutionGroup.Members.Count; ++i) {
|
|
XmlSchemaElement element = (XmlSchemaElement)substitutionGroup.Members[i];
|
|
|
|
//Chain to other head's that are members of this head's substGroup
|
|
XmlSchemaSubstitutionGroup g = (XmlSchemaSubstitutionGroup)substTable[element.QualifiedName];
|
|
if (g != null) {
|
|
ResolveSubstitutionGroup(g, substTable);
|
|
for (int j = 0; j < g.Members.Count; ++j) {
|
|
XmlSchemaElement element1 = (XmlSchemaElement)g.Members[j];
|
|
if (element1 != element) { //Exclude the head
|
|
if (newMembers == null) {
|
|
newMembers = new List<XmlSchemaElement>();
|
|
}
|
|
newMembers.Add(element1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (newMembers != null) {
|
|
for (int i = 0; i < newMembers.Count; ++i) {
|
|
substitutionGroup.Members.Add(newMembers[i]);
|
|
}
|
|
}
|
|
substitutionGroup.Members.Add(headElement);
|
|
}
|
|
|
|
internal XmlSchema Remove(XmlSchema schema, bool forceCompile) {
|
|
// Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Reprocess
|
|
// method. If you change anything here *make sure* to update Reprocess method accordingly.
|
|
if (schema == null) {
|
|
throw new ArgumentNullException("schema");
|
|
}
|
|
lock (InternalSyncObject) { //Need to lock here so that remove cannot be called while the set is being compiled
|
|
if (schemas.ContainsKey(schema.SchemaId)) {
|
|
|
|
// This code is copied to method:
|
|
// Reprocess(XmlSchema schema)
|
|
// If you changed anything here go and change the same in Reprocess(XmlSchema schema) method
|
|
#region This code is copied to Reprocess(XmlSchema schema) method
|
|
|
|
if (forceCompile) {
|
|
RemoveSchemaFromGlobalTables(schema);
|
|
RemoveSchemaFromCaches(schema);
|
|
}
|
|
schemas.Remove(schema.SchemaId);
|
|
if (schema.BaseUri != null) {
|
|
schemaLocations.Remove(schema.BaseUri);
|
|
}
|
|
string tns = GetTargetNamespace(schema);
|
|
if (Schemas(tns).Count == 0) { //This is the only schema for that namespace
|
|
targetNamespaces.Remove(tns);
|
|
}
|
|
if (forceCompile) {
|
|
isCompiled = false;
|
|
compileAll = true; //Force compilation of the whole set; This is when the set is not completely thread-safe
|
|
}
|
|
return schema;
|
|
|
|
#endregion // This code is copied to Reprocess(XmlSchema schema) method
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void ClearTables() {
|
|
GlobalElements.Clear();
|
|
GlobalAttributes.Clear();
|
|
GlobalTypes.Clear();
|
|
SubstitutionGroups.Clear();
|
|
TypeExtensions.Clear();
|
|
}
|
|
|
|
internal bool PreprocessSchema(ref XmlSchema schema, string targetNamespace) {
|
|
Preprocessor prep = new Preprocessor(nameTable, GetSchemaNames(nameTable), eventHandler, compilationSettings);
|
|
prep.XmlResolver = readerSettings.GetXmlResolver_CheckConfig();
|
|
prep.ReaderSettings = readerSettings;
|
|
prep.SchemaLocations = schemaLocations;
|
|
prep.ChameleonSchemas = chameleonSchemas;
|
|
bool hasErrors = prep.Execute(schema, targetNamespace, true);
|
|
schema = prep.RootSchema; //For any root level chameleon cloned
|
|
return hasErrors;
|
|
}
|
|
|
|
internal XmlSchema ParseSchema(string targetNamespace, XmlReader reader) {
|
|
XmlNameTable readerNameTable = reader.NameTable;
|
|
SchemaNames schemaNames = GetSchemaNames(readerNameTable);
|
|
Parser parser = new Parser(SchemaType.XSD, readerNameTable, schemaNames, eventHandler);
|
|
parser.XmlResolver = readerSettings.GetXmlResolver_CheckConfig();
|
|
SchemaType schemaType;
|
|
try {
|
|
schemaType = parser.Parse(reader, targetNamespace);
|
|
}
|
|
catch(XmlSchemaException e) {
|
|
SendValidationEvent(e, XmlSeverityType.Error);
|
|
return null;
|
|
}
|
|
return parser.XmlSchema;
|
|
}
|
|
|
|
internal void CopyFromCompiledSet(XmlSchemaSet otherSet) {
|
|
XmlSchema currentSchema;
|
|
SortedList copyFromList = otherSet.SortedSchemas;
|
|
bool setIsCompiled = schemas.Count == 0 ? true : false;
|
|
ArrayList existingSchemas = new ArrayList();
|
|
|
|
SchemaInfo newCompiledInfo = new SchemaInfo();
|
|
Uri baseUri;
|
|
for(int i=0; i < copyFromList.Count; i++) {
|
|
currentSchema = (XmlSchema)copyFromList.GetByIndex(i);
|
|
baseUri = currentSchema.BaseUri;
|
|
if (schemas.ContainsKey(currentSchema.SchemaId) || (baseUri != null && baseUri.OriginalString.Length != 0 && schemaLocations[baseUri] != null)) {
|
|
existingSchemas.Add(currentSchema);
|
|
continue;
|
|
}
|
|
schemas.Add(currentSchema.SchemaId, currentSchema);
|
|
if (baseUri != null && baseUri.OriginalString.Length != 0) {
|
|
schemaLocations.Add(baseUri, currentSchema);
|
|
}
|
|
string tns = GetTargetNamespace(currentSchema);
|
|
if (targetNamespaces[tns] == null) {
|
|
targetNamespaces.Add(tns, tns);
|
|
}
|
|
}
|
|
|
|
VerifyTables();
|
|
foreach (XmlSchemaElement element in otherSet.GlobalElements.Values) {
|
|
if(!AddToTable(elements, element.QualifiedName, element)) {
|
|
goto RemoveAll;
|
|
}
|
|
}
|
|
foreach (XmlSchemaAttribute attribute in otherSet.GlobalAttributes.Values) {
|
|
if (!AddToTable(attributes, attribute.QualifiedName, attribute)) {
|
|
goto RemoveAll;
|
|
}
|
|
}
|
|
foreach (XmlSchemaType schemaType in otherSet.GlobalTypes.Values) {
|
|
if (!AddToTable(schemaTypes, schemaType.QualifiedName, schemaType)) {
|
|
goto RemoveAll;
|
|
}
|
|
}
|
|
//
|
|
ProcessNewSubstitutionGroups(otherSet.SubstitutionGroups, false);
|
|
|
|
newCompiledInfo.Add(cachedCompiledInfo, eventHandler); //Add all the items from the old to the new compiled object
|
|
newCompiledInfo.Add(otherSet.CompiledInfo,eventHandler); //
|
|
cachedCompiledInfo = newCompiledInfo; //Replace the compiled info in the set after successful compilation
|
|
if (setIsCompiled) {
|
|
isCompiled = true;
|
|
compileAll = false;
|
|
}
|
|
return;
|
|
|
|
RemoveAll:
|
|
foreach (XmlSchema schemaToRemove in copyFromList.Values) {
|
|
if (!existingSchemas.Contains(schemaToRemove)) {
|
|
Remove(schemaToRemove, false);
|
|
}
|
|
}
|
|
foreach (XmlSchemaElement elementToRemove in otherSet.GlobalElements.Values) {
|
|
if(!existingSchemas.Contains((XmlSchema)elementToRemove.Parent)) {
|
|
elements.Remove(elementToRemove.QualifiedName);
|
|
}
|
|
}
|
|
foreach (XmlSchemaAttribute attributeToRemove in otherSet.GlobalAttributes.Values) {
|
|
if(!existingSchemas.Contains((XmlSchema)attributeToRemove.Parent)) {
|
|
attributes.Remove(attributeToRemove.QualifiedName);
|
|
}
|
|
}
|
|
foreach (XmlSchemaType schemaTypeToRemove in otherSet.GlobalTypes.Values) {
|
|
if(!existingSchemas.Contains((XmlSchema)schemaTypeToRemove.Parent)) {
|
|
schemaTypes.Remove(schemaTypeToRemove.QualifiedName);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal SchemaInfo CompiledInfo {
|
|
get {
|
|
return cachedCompiledInfo;
|
|
}
|
|
}
|
|
|
|
internal XmlReaderSettings ReaderSettings {
|
|
get {
|
|
return readerSettings;
|
|
}
|
|
}
|
|
|
|
internal XmlResolver GetResolver() {
|
|
return readerSettings.GetXmlResolver_CheckConfig();
|
|
}
|
|
|
|
internal ValidationEventHandler GetEventHandler() {
|
|
return eventHandler;
|
|
}
|
|
|
|
internal SchemaNames GetSchemaNames(XmlNameTable nt) {
|
|
if (nameTable != nt) {
|
|
return new SchemaNames(nt);
|
|
}
|
|
else {
|
|
if (schemaNames == null) {
|
|
schemaNames = new SchemaNames( nameTable );
|
|
}
|
|
return schemaNames;
|
|
}
|
|
}
|
|
|
|
internal bool IsSchemaLoaded(Uri schemaUri, string targetNamespace, out XmlSchema schema) {
|
|
schema = null;
|
|
if (targetNamespace == null) {
|
|
targetNamespace = string.Empty;
|
|
}
|
|
if (GetSchemaByUri(schemaUri, out schema)) {
|
|
if (schemas.ContainsKey(schema.SchemaId) && (targetNamespace.Length == 0 || targetNamespace == schema.TargetNamespace)) { //schema is present in set
|
|
//Schema found
|
|
}
|
|
else if (schema.TargetNamespace == null) { //If schema not in set or namespace doesnt match, then it might be a chameleon
|
|
XmlSchema chameleonSchema = FindSchemaByNSAndUrl(schemaUri, targetNamespace, null);
|
|
if (chameleonSchema != null && schemas.ContainsKey(chameleonSchema.SchemaId)) {
|
|
schema = chameleonSchema;
|
|
}
|
|
else {
|
|
schema = Add(targetNamespace, schema);
|
|
}
|
|
}
|
|
else if (targetNamespace.Length != 0 && targetNamespace != schema.TargetNamespace) {
|
|
SendValidationEvent(new XmlSchemaException(Res.Sch_MismatchTargetNamespaceEx, new string[] { targetNamespace, schema.TargetNamespace }), XmlSeverityType.Error);
|
|
schema = null;
|
|
}
|
|
else {
|
|
//If here, schema not present in set but in loc and might be added in loc through an earlier include
|
|
//S.TNS != null && ( tns == null or tns == s.TNS)
|
|
AddSchemaToSet(schema);
|
|
}
|
|
return true; //Schema Found
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal bool GetSchemaByUri(Uri schemaUri, out XmlSchema schema) {
|
|
schema = null;
|
|
if (schemaUri == null || schemaUri.OriginalString.Length == 0) {
|
|
return false;
|
|
}
|
|
schema = (XmlSchema)schemaLocations[schemaUri];
|
|
if (schema != null) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal string GetTargetNamespace(XmlSchema schema) {
|
|
return schema.TargetNamespace == null ? string.Empty : schema.TargetNamespace;
|
|
}
|
|
|
|
|
|
internal SortedList SortedSchemas {
|
|
get {
|
|
return schemas;
|
|
}
|
|
}
|
|
|
|
internal bool CompileAll {
|
|
get {
|
|
return compileAll;
|
|
}
|
|
}
|
|
|
|
//Private Methods
|
|
private void RemoveSchemaFromCaches(XmlSchema schema) {
|
|
//Remove From ChameleonSchemas and schemaLocations cache
|
|
List<XmlSchema> reprocessList = new List<XmlSchema>();
|
|
schema.GetExternalSchemasList(reprocessList, schema);
|
|
for (int i = 0; i < reprocessList.Count; ++i) { //Remove schema from schemaLocations & chameleonSchemas tables
|
|
if (reprocessList[i].BaseUri != null && reprocessList[i].BaseUri.OriginalString.Length != 0) {
|
|
schemaLocations.Remove(reprocessList[i].BaseUri);
|
|
}
|
|
//Remove from chameleon table
|
|
ICollection chameleonKeys = chameleonSchemas.Keys;
|
|
ArrayList removalList = new ArrayList();
|
|
foreach(ChameleonKey cKey in chameleonKeys) {
|
|
if (cKey.chameleonLocation.Equals(reprocessList[i].BaseUri)) {
|
|
// The key will have the originalSchema set to null if the location was not empty
|
|
// otherwise we need to care about it as there may be more chameleon schemas without
|
|
// a base URI and we want to remove only those which were created as a clone of the one
|
|
// we're removing.
|
|
if (cKey.originalSchema == null || Ref.ReferenceEquals(cKey.originalSchema, reprocessList[i])) {
|
|
removalList.Add(cKey);
|
|
}
|
|
}
|
|
}
|
|
for (int j = 0; j < removalList.Count; ++j) {
|
|
chameleonSchemas.Remove(removalList[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RemoveSchemaFromGlobalTables(XmlSchema schema) {
|
|
if (schemas.Count == 0) {
|
|
return;
|
|
}
|
|
VerifyTables();
|
|
foreach (XmlSchemaElement elementToRemove in schema.Elements.Values) {
|
|
XmlSchemaElement elem = (XmlSchemaElement)elements[elementToRemove.QualifiedName];
|
|
if (elem == elementToRemove) {
|
|
elements.Remove(elementToRemove.QualifiedName);
|
|
}
|
|
}
|
|
foreach (XmlSchemaAttribute attributeToRemove in schema.Attributes.Values) {
|
|
XmlSchemaAttribute attr = (XmlSchemaAttribute)attributes[attributeToRemove.QualifiedName];
|
|
if (attr == attributeToRemove) {
|
|
attributes.Remove(attributeToRemove.QualifiedName);
|
|
}
|
|
}
|
|
foreach (XmlSchemaType schemaTypeToRemove in schema.SchemaTypes.Values) {
|
|
XmlSchemaType schemaType = (XmlSchemaType)schemaTypes[schemaTypeToRemove.QualifiedName];
|
|
if (schemaType == schemaTypeToRemove) {
|
|
schemaTypes.Remove(schemaTypeToRemove.QualifiedName);
|
|
}
|
|
}
|
|
}
|
|
private bool AddToTable(XmlSchemaObjectTable table, XmlQualifiedName qname, XmlSchemaObject item) {
|
|
if (qname.Name.Length == 0) {
|
|
return true;
|
|
}
|
|
XmlSchemaObject existingObject = (XmlSchemaObject)table[qname];
|
|
if (existingObject != null) {
|
|
if (existingObject == item || existingObject.SourceUri == item.SourceUri) {
|
|
return true;
|
|
}
|
|
string code = string.Empty;
|
|
if (item is XmlSchemaComplexType) {
|
|
code = Res.Sch_DupComplexType;
|
|
}
|
|
else if (item is XmlSchemaSimpleType) {
|
|
code = Res.Sch_DupSimpleType;
|
|
}
|
|
else if (item is XmlSchemaElement) {
|
|
code = Res.Sch_DupGlobalElement;
|
|
}
|
|
else if (item is XmlSchemaAttribute) {
|
|
if (qname.Namespace == XmlReservedNs.NsXml) {
|
|
XmlSchema schemaForXmlNS = Preprocessor.GetBuildInSchema();
|
|
XmlSchemaObject builtInAttribute = schemaForXmlNS.Attributes[qname];
|
|
if (existingObject == builtInAttribute) { //replace built-in one
|
|
table.Insert(qname, item);
|
|
return true;
|
|
}
|
|
else if (item == builtInAttribute) { //trying to overwrite customer's component with built-in, ignore built-in
|
|
return true;
|
|
}
|
|
}
|
|
code = Res.Sch_DupGlobalAttribute;
|
|
}
|
|
SendValidationEvent(new XmlSchemaException(code,qname.ToString()), XmlSeverityType.Error);
|
|
return false;
|
|
}
|
|
else {
|
|
table.Add(qname, item);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private void VerifyTables() {
|
|
if (elements == null) {
|
|
elements = new XmlSchemaObjectTable();
|
|
}
|
|
if (attributes == null) {
|
|
attributes = new XmlSchemaObjectTable();
|
|
}
|
|
if (schemaTypes == null) {
|
|
schemaTypes = new XmlSchemaObjectTable();
|
|
}
|
|
if (substitutionGroups == null) {
|
|
substitutionGroups = new XmlSchemaObjectTable();
|
|
}
|
|
}
|
|
|
|
private void InternalValidationCallback(object sender, ValidationEventArgs e ) {
|
|
if (e.Severity == XmlSeverityType.Error) {
|
|
throw e.Exception;
|
|
}
|
|
}
|
|
|
|
private void SendValidationEvent(XmlSchemaException e, XmlSeverityType severity) {
|
|
if (eventHandler != null) {
|
|
eventHandler(this, new ValidationEventArgs(e, severity));
|
|
}
|
|
else {
|
|
throw e;
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
}
|