//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // Microsoft //------------------------------------------------------------------------------ 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 /// /// /// 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. /// public class XmlSchemaSet { XmlNameTable nameTable; SchemaNames schemaNames; SortedList schemas; // List of source schemas //Event handling ValidationEventHandler internalEventHandler; ValidationEventHandler eventHandler; bool isCompiled = false; //Dictionary schemaLocations; //Dictionary 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(ref internalSyncObject, o, null); } return internalSyncObject; } } //Constructors /// /// /// Construct a new empty schema schemas. /// public XmlSchemaSet() : this(new NameTable()) { } /// /// /// Construct a new empty schema schemas with associated XmlNameTable. /// The XmlNameTable is used when loading schemas /// public XmlSchemaSet(XmlNameTable nameTable) { if (nameTable == null) { throw new ArgumentNullException("nameTable"); } this.nameTable = nameTable; schemas = new SortedList(); /*schemaLocations = new Dictionary(); chameleonSchemas = new Dictionary();*/ 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 /// /// /// The default XmlNameTable used by the XmlSchemaSet when loading new schemas. /// public XmlNameTable NameTable { get { return nameTable;} } /// public event ValidationEventHandler ValidationEventHandler { add { eventHandler -= internalEventHandler; eventHandler += value; if (eventHandler == null) { eventHandler = internalEventHandler; } } remove { eventHandler -= value; if (eventHandler == null) { eventHandler = internalEventHandler; } } } /// /// /// IsCompiled is true when the schema set is in compiled state /// public bool IsCompiled { get { return isCompiled; } } /// /// /// /// public XmlResolver XmlResolver { set { readerSettings.XmlResolver = value; } } /// /// /// /// public XmlSchemaCompilationSettings CompilationSettings { get { return compilationSettings; } set { compilationSettings = value; } } /// /// /// Returns the count of schemas in the set /// public int Count { get { return schemas.Count; } } /// /// /// /// public XmlSchemaObjectTable GlobalElements { get { if (elements == null) { elements = new XmlSchemaObjectTable(); } return elements; } } /// /// /// /// public XmlSchemaObjectTable GlobalAttributes { get { if (attributes == null) { attributes = new XmlSchemaObjectTable(); } return attributes; } } /// /// /// /// public XmlSchemaObjectTable GlobalTypes { get { if (schemaTypes == null) { schemaTypes = new XmlSchemaObjectTable(); } return schemaTypes; } } /// /// /// /// /// internal XmlSchemaObjectTable SubstitutionGroups { get { if (substitutionGroups == null) { substitutionGroups = new XmlSchemaObjectTable(); } return substitutionGroups; } } /// /// Table of all types extensions /// internal Hashtable SchemaLocations { get { return schemaLocations; } } /// /// Table of all types extensions /// internal XmlSchemaObjectTable TypeExtensions { get { if (typeExtensions == null) { typeExtensions = new XmlSchemaObjectTable(); } return typeExtensions; } } //Public Methods /// /// /// 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. /// [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; } /// /// /// 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. /// 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; } } } /// /// /// Adds all the namespaces defined in the given schemas /// (including their associated schemas) to this schemas. /// 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); } } } /// 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); } } /// public XmlSchema Remove(XmlSchema schema) { return Remove(schema, true); } /// 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; } /// public bool Contains(String targetNamespace) { if (targetNamespace == null) { targetNamespace = string.Empty; } return targetNamespaces[targetNamespace] != null; } /// public bool Contains(XmlSchema schema) { if (schema == null) { throw new ArgumentNullException("schema"); } return schemas.ContainsValue(schema); } /// /// /// [To be supplied.] /// 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; } /// /// /// [To be supplied.] /// public XmlSchema Reprocess(XmlSchema schema) { // Due to 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; } } /// /// /// [To be supplied.] /// 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); } /// /// /// [To be supplied.] /// public ICollection Schemas() { return schemas.Values; } /// /// /// [To be supplied.] /// 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 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 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 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(); } 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 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 reprocessList = new List(); 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 }