File: System\Xml\Schema\Chameleonkey.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Xml.Serialization;
 
namespace System.Xml.Schema
{
    // Case insensitive file name key for use in a hashtable.
 
    internal sealed class ChameleonKey
    {
        internal string targetNS;
        internal Uri chameleonLocation;
        // Original schema (used for reference equality only)
        //   stored only when the chameleonLocation is an empty URI in which case the location
        //   is not a good enough identification of the schema
        internal XmlSchema? originalSchema;
        private int _hashCode;
 
        /// <summary>
        /// Creates a new chameleon key - an identification for a chameleon schema instance
        /// </summary>
        /// <param name="ns">The target namespace of the instance of the chameleon schema</param>
        /// <param name="originalSchema">The original (chameleon) schema (the one without the target namespace).
        ///   This is used to get the location (base uri) and to identify the schema.</param>
        public ChameleonKey(string ns, XmlSchema originalSchema)
        {
            targetNS = ns;
            chameleonLocation = originalSchema.BaseUri!;
            if (chameleonLocation.OriginalString.Length == 0)
            {
                // Only store the original schema when the location is empty URI
                //   by doing this we effectively allow multiple chameleon schemas for the same target namespace
                //   and URI, but that only makes sense for empty URI (not specified)
                this.originalSchema = originalSchema;
            }
        }
 
        public override int GetHashCode()
        {
            if (_hashCode == 0)
            {
                _hashCode = unchecked(targetNS.GetHashCode() + chameleonLocation.GetHashCode() +
                    (originalSchema == null ? 0 : originalSchema.GetHashCode()));
            }
            return _hashCode;
        }
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            if (Ref.ReferenceEquals(this, obj))
            {
                return true;
            }
            ChameleonKey? cKey = obj as ChameleonKey;
            if (cKey != null)
            {
                // We want to compare the target NS and the schema location.
                // If the location is empty (but only then) we also want to compare the original schema instance.
                // As noted above the originalSchema is null if the chameleonLocation is non-empty. As a result we
                // can simply compare the reference to the original schema always (regardless of the schemalocation).
                Debug.Assert((chameleonLocation.OriginalString.Length == 0 && originalSchema != null)
                    || (chameleonLocation.OriginalString.Length != 0 && originalSchema == null));
                return this.targetNS.Equals(cKey.targetNS) && this.chameleonLocation.Equals(cKey.chameleonLocation) &&
                    Ref.ReferenceEquals(originalSchema, cKey.originalSchema);
            }
            return false;
        }
    }
}