File: System\Xml\Serialization\ImportContext.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;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
 
namespace System.Xml.Serialization
{
    public class ImportContext
    {
        private readonly bool _shareTypes;
        private SchemaObjectCache? _cache; // cached schema top-level items
        private Hashtable? _mappings; // XmlSchema -> SerializableMapping, XmlSchemaSimpleType -> EnumMapping, XmlSchemaComplexType -> StructMapping
        private Hashtable? _elements; // XmlSchemaElement -> ElementAccessor
        private CodeIdentifiers? _typeIdentifiers;
 
        public ImportContext(CodeIdentifiers? identifiers, bool shareTypes)
        {
            _typeIdentifiers = identifiers;
            _shareTypes = shareTypes;
        }
 
        internal ImportContext() : this(null, false) { }
 
        internal SchemaObjectCache Cache => _cache ??= new SchemaObjectCache();
 
        internal Hashtable Elements => _elements ??= new Hashtable();
 
        internal Hashtable Mappings => _mappings ??= new Hashtable();
 
        public CodeIdentifiers TypeIdentifiers => _typeIdentifiers ??= new CodeIdentifiers();
 
        public bool ShareTypes
        {
            get { return _shareTypes; }
        }
 
        public StringCollection Warnings
        {
            get { return Cache.Warnings; }
        }
    }
 
    internal sealed class SchemaObjectCache
    {
        private Hashtable? _graph;
        private Hashtable? _hash;
        private Hashtable? _objectCache;
        private StringCollection? _warnings;
        // UNDONE remove me soon, this is debug only code
        internal Hashtable looks = new Hashtable();
        private Hashtable Graph => _graph ??= new Hashtable();
 
        private Hashtable Hash => _hash ??= new Hashtable();
 
        private Hashtable ObjectCache => _objectCache ??= new Hashtable();
 
        internal StringCollection Warnings => _warnings ??= new StringCollection();
 
        internal XmlSchemaObject? AddItem(XmlSchemaObject? item, XmlQualifiedName? qname)
        {
            if (item == null)
                return null;
            if (qname == null || qname.IsEmpty)
                return null;
 
            string key = $"{item.GetType().Name}:{qname}";
            ArrayList? list = (ArrayList?)ObjectCache[key];
            if (list == null)
            {
                list = new ArrayList();
                ObjectCache[key] = list;
            }
 
            for (int i = 0; i < list.Count; i++)
            {
                XmlSchemaObject cachedItem = (XmlSchemaObject)list[i]!;
                if (cachedItem == item)
                    return cachedItem;
 
                if (Match(cachedItem, item, true))
                {
                    return cachedItem;
                }
                else
                {
                    Warnings.Add(SR.Format(SR.XmlMismatchSchemaObjects, item.GetType().Name, qname.Name, qname.Namespace));
                    Warnings.Add($"DEBUG:Cached item key:\r\n{(string?)looks[cachedItem]}\r\nnew item key:\r\n{(string?)looks[item]}");
                }
            }
            // no match found we need to insert the new type in the cache
            list.Add(item);
            return item;
        }
 
        internal bool Match(XmlSchemaObject o1, XmlSchemaObject o2, bool shareTypes)
        {
            if (o1 == o2)
                return true;
            if (o1.GetType() != o2.GetType())
                return false;
 
            Hash[o1] ??= GetHash(o1);
            int hash1 = (int)Hash[o1]!;
            int hash2 = GetHash(o2);
            if (hash1 != hash2)
                return false;
 
            if (shareTypes)
                return CompositeHash(o1) == CompositeHash(o2);
            return true;
        }
 
        private ArrayList GetDependencies(XmlSchemaObject o, ArrayList deps, Hashtable refs)
        {
            if (refs[o] == null)
            {
                refs[o] = o;
                deps.Add(o);
                ArrayList? list = Graph[o] as ArrayList;
                if (list != null)
                {
                    for (int i = 0; i < list.Count; i++)
                    {
                        GetDependencies((XmlSchemaObject)list[i]!, deps, refs);
                    }
                }
            }
            return deps;
        }
 
        private int CompositeHash(XmlSchemaObject o)
        {
            ArrayList list = GetDependencies(o, new ArrayList(), new Hashtable());
            double tmp = 0;
            for (int i = 0; i < list.Count; i++)
            {
                object? cachedHash = Hash[list[i]!];
                if (cachedHash is int)
                {
                    tmp += (int)cachedHash / list.Count;
                }
            }
            return (int)tmp;
        }
 
        [RequiresUnreferencedCode("creates SchemaGraph")]
        [RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
        internal void GenerateSchemaGraph(XmlSchemas schemas)
        {
            SchemaGraph graph = new SchemaGraph(Graph, schemas);
            ArrayList items = graph.GetItems();
 
            for (int i = 0; i < items.Count; i++)
            {
                GetHash((XmlSchemaObject)items[i]!);
            }
        }
 
        private int GetHash(XmlSchemaObject o)
        {
            object? hash = Hash[o];
            if (hash != null)
            {
                if (hash is XmlSchemaObject)
                {
                }
                else
                {
                    return (int)hash;
                }
            }
            // new object, generate the hash
            string hashString = ToString(o, new SchemaObjectWriter());
            looks[o] = hashString;
            int code = hashString.GetHashCode();
            Hash[o] = code;
            return code;
        }
 
        private static string ToString(XmlSchemaObject o, SchemaObjectWriter writer)
        {
            return writer.WriteXmlSchemaObject(o);
        }
    }
 
    internal sealed class SchemaGraph
    {
        private readonly ArrayList _empty = new ArrayList();
        private readonly XmlSchemas _schemas;
        private readonly Hashtable _scope;
        private readonly int _items;
 
        [RequiresUnreferencedCode("Calls Compile")]
        [RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
        internal SchemaGraph(Hashtable scope, XmlSchemas schemas)
        {
            _scope = scope;
            schemas.Compile(null, false);
            _schemas = schemas;
            _items = 0;
            foreach (XmlSchema s in schemas)
            {
                _items += s.Items.Count;
                foreach (XmlSchemaObject item in s.Items)
                {
                    Depends(item);
                }
            }
        }
 
        internal ArrayList GetItems()
        {
            return new ArrayList(_scope.Keys);
        }
 
        internal void AddRef(ArrayList list, XmlSchemaObject? o)
        {
            if (o == null)
                return;
            if (_schemas.IsReference(o))
                return;
            if (o.Parent is XmlSchema parent)
            {
                string? ns = parent.TargetNamespace;
                if (ns == XmlSchema.Namespace)
                    return;
                if (list.Contains(o))
                    return;
                list.Add(o);
            }
        }
 
        internal ArrayList Depends(XmlSchemaObject item)
        {
            if (item.Parent is XmlSchema)
            {
                if (_scope[item] != null)
                    return (ArrayList)_scope[item]!;
 
                ArrayList refs = new ArrayList();
                Depends(item, refs);
                _scope.Add(item, refs);
                return refs;
            }
            return _empty;
        }
 
        internal void Depends(XmlSchemaObject? item, ArrayList refs)
        {
            if (item == null || _scope[item] != null)
                return;
 
            Type t = item.GetType();
            if (typeof(XmlSchemaType).IsAssignableFrom(t))
            {
                XmlQualifiedName baseName = XmlQualifiedName.Empty;
                XmlSchemaType? baseType = null;
                XmlSchemaParticle? particle = null;
                XmlSchemaObjectCollection? attributes = null;
 
                if (item is XmlSchemaComplexType ct)
                {
                    if (ct.ContentModel != null)
                    {
                        XmlSchemaContent? content = ct.ContentModel.Content;
                        if (content is XmlSchemaComplexContentRestriction)
                        {
                            baseName = ((XmlSchemaComplexContentRestriction)content).BaseTypeName;
                            attributes = ((XmlSchemaComplexContentRestriction)content).Attributes;
                        }
                        else if (content is XmlSchemaSimpleContentRestriction restriction)
                        {
                            if (restriction.BaseType != null)
                                baseType = restriction.BaseType;
                            else
                                baseName = restriction.BaseTypeName;
                            attributes = restriction.Attributes;
                        }
                        else if (content is XmlSchemaComplexContentExtension complex)
                        {
                            attributes = complex.Attributes;
                            particle = complex.Particle;
                            baseName = complex.BaseTypeName;
                        }
                        else if (content is XmlSchemaSimpleContentExtension simple)
                        {
                            attributes = simple.Attributes;
                            baseName = simple.BaseTypeName;
                        }
                    }
                    else
                    {
                        attributes = ct.Attributes;
                        particle = ct.Particle;
                    }
                    if (particle is XmlSchemaGroupRef refGroup)
                    {
                        particle = ((XmlSchemaGroup)_schemas.Find(refGroup.RefName, typeof(XmlSchemaGroup), false)!).Particle;
                    }
                    else if (particle is XmlSchemaGroupBase)
                    {
                        particle = (XmlSchemaGroupBase)particle;
                    }
                }
                else if (item is XmlSchemaSimpleType simpleType)
                {
                    XmlSchemaSimpleTypeContent? content = simpleType.Content;
                    if (content is XmlSchemaSimpleTypeRestriction)
                    {
                        baseType = ((XmlSchemaSimpleTypeRestriction)content).BaseType;
                        baseName = ((XmlSchemaSimpleTypeRestriction)content).BaseTypeName;
                    }
                    else if (content is XmlSchemaSimpleTypeList list)
                    {
                        if (list.ItemTypeName != null && !list.ItemTypeName.IsEmpty)
                            baseName = list.ItemTypeName;
                        if (list.ItemType != null)
                        {
                            baseType = list.ItemType;
                        }
                    }
                    else if (t == typeof(XmlSchemaSimpleTypeUnion))
                    {
                        XmlQualifiedName[]? memberTypes = ((XmlSchemaSimpleTypeUnion)item).MemberTypes;
 
                        if (memberTypes != null)
                        {
                            for (int i = 0; i < memberTypes.Length; i++)
                            {
                                XmlSchemaType? type = (XmlSchemaType?)_schemas.Find(memberTypes[i], typeof(XmlSchemaType), false);
                                AddRef(refs, type);
                            }
                        }
                    }
                }
                if (baseType == null && !baseName.IsEmpty && baseName.Namespace != XmlSchema.Namespace)
                    baseType = (XmlSchemaType?)_schemas.Find(baseName, typeof(XmlSchemaType), false);
 
                if (baseType != null)
                {
                    AddRef(refs, baseType);
                }
                if (particle != null)
                {
                    Depends(particle, refs);
                }
                if (attributes != null)
                {
                    for (int i = 0; i < attributes.Count; i++)
                    {
                        Depends(attributes[i], refs);
                    }
                }
            }
            else if (t == typeof(XmlSchemaElement))
            {
                XmlSchemaElement el = (XmlSchemaElement)item;
                if (!el.SubstitutionGroup.IsEmpty)
                {
                    if (el.SubstitutionGroup.Namespace != XmlSchema.Namespace)
                    {
                        XmlSchemaElement? head = (XmlSchemaElement?)_schemas.Find(el.SubstitutionGroup, typeof(XmlSchemaElement), false);
                        AddRef(refs, head);
                    }
                }
                if (!el.RefName.IsEmpty)
                {
                    el = (XmlSchemaElement)_schemas.Find(el.RefName, typeof(XmlSchemaElement), false)!;
                    AddRef(refs, el);
                }
                else if (!el.SchemaTypeName.IsEmpty)
                {
                    XmlSchemaType? type = (XmlSchemaType?)_schemas.Find(el.SchemaTypeName, typeof(XmlSchemaType), false);
                    AddRef(refs, type);
                }
                else
                {
                    Depends(el.SchemaType, refs);
                }
            }
            else if (t == typeof(XmlSchemaGroup))
            {
                Depends(((XmlSchemaGroup)item).Particle!);
            }
            else if (t == typeof(XmlSchemaGroupRef))
            {
                XmlSchemaGroup? group = (XmlSchemaGroup?)_schemas.Find(((XmlSchemaGroupRef)item).RefName, typeof(XmlSchemaGroup), false);
                AddRef(refs, group);
            }
            else if (typeof(XmlSchemaGroupBase).IsAssignableFrom(t))
            {
                foreach (XmlSchemaObject o in ((XmlSchemaGroupBase)item).Items)
                {
                    Depends(o, refs);
                }
            }
            else if (t == typeof(XmlSchemaAttributeGroupRef))
            {
                XmlSchemaAttributeGroup? group = (XmlSchemaAttributeGroup?)_schemas.Find(((XmlSchemaAttributeGroupRef)item).RefName, typeof(XmlSchemaAttributeGroup), false);
                AddRef(refs, group);
            }
            else if (t == typeof(XmlSchemaAttributeGroup))
            {
                foreach (XmlSchemaObject o in ((XmlSchemaAttributeGroup)item).Attributes)
                {
                    Depends(o, refs);
                }
            }
            else if (t == typeof(XmlSchemaAttribute))
            {
                XmlSchemaAttribute? at = (XmlSchemaAttribute)item;
                if (!at.RefName.IsEmpty)
                {
                    at = (XmlSchemaAttribute?)_schemas.Find(at.RefName, typeof(XmlSchemaAttribute), false);
                    AddRef(refs, at);
                }
                else if (!at.SchemaTypeName.IsEmpty)
                {
                    XmlSchemaType? type = (XmlSchemaType?)_schemas.Find(at.SchemaTypeName, typeof(XmlSchemaType), false);
                    AddRef(refs, type);
                }
                else
                {
                    Depends(at.SchemaType, refs);
                }
            }
            if (typeof(XmlSchemaAnnotated).IsAssignableFrom(t))
            {
                XmlAttribute[]? attrs = (XmlAttribute[]?)((XmlSchemaAnnotated)item).UnhandledAttributes;
 
                if (attrs != null)
                {
                    for (int i = 0; i < attrs.Length; i++)
                    {
                        XmlAttribute attribute = attrs[i];
                        if (attribute.LocalName == Wsdl.ArrayType && attribute.NamespaceURI == Wsdl.Namespace)
                        {
                            XmlQualifiedName qname = TypeScope.ParseWsdlArrayType(attribute.Value, out _, item);
                            XmlSchemaType? type = (XmlSchemaType?)_schemas.Find(qname, typeof(XmlSchemaType), false);
                            AddRef(refs, type);
                        }
                    }
                }
            }
        }
    }
}