File: System\Xml\Serialization\XmlSerializationGeneratedCode.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.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security;
using System.Threading;
 
namespace System.Xml.Serialization
{
    ///<internalonly/>
    public abstract class XmlSerializationGeneratedCode
    {
    }
    internal class XmlSerializationCodeGen
    {
        private readonly IndentedWriter _writer;
        private int _nextMethodNumber;
        private readonly Hashtable _methodNames = new Hashtable();
        private readonly ReflectionAwareCodeGen _raCodeGen;
        private readonly TypeScope[] _scopes;
        private readonly TypeDesc? _stringTypeDesc;
        private readonly TypeDesc? _qnameTypeDesc;
        private readonly string _access;
        private readonly string _className;
        private TypeMapping[]? _referencedMethods;
        private int _references;
        private readonly Hashtable _generatedMethods = new Hashtable();
 
        [RequiresUnreferencedCode("Calls GetTypeDesc")]
        internal XmlSerializationCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className)
        {
            _writer = writer;
            _scopes = scopes;
            if (scopes.Length > 0)
            {
                _stringTypeDesc = scopes[0].GetTypeDesc(typeof(string));
                _qnameTypeDesc = scopes[0].GetTypeDesc(typeof(XmlQualifiedName));
            }
            _raCodeGen = new ReflectionAwareCodeGen(writer);
            _className = className;
            _access = access;
        }
 
        internal IndentedWriter Writer { get { return _writer; } }
        internal int NextMethodNumber { get { return _nextMethodNumber; } set { _nextMethodNumber = value; } }
        internal ReflectionAwareCodeGen RaCodeGen { get { return _raCodeGen; } }
        internal TypeDesc? StringTypeDesc { get { return _stringTypeDesc; } }
        internal TypeDesc? QnameTypeDesc { get { return _qnameTypeDesc; } }
        internal string ClassName { get { return _className; } }
        internal string Access { get { return _access; } }
        internal TypeScope[] Scopes { get { return _scopes; } }
        internal Hashtable MethodNames { get { return _methodNames; } }
        internal Hashtable GeneratedMethods { get { return _generatedMethods; } }
 
        [RequiresUnreferencedCode("calls WriteStructMethod")]
        internal virtual void GenerateMethod(TypeMapping mapping) { }
 
        [RequiresUnreferencedCode("calls GenerateMethod")]
        internal void GenerateReferencedMethods()
        {
            while (_references > 0)
            {
                TypeMapping mapping = _referencedMethods![--_references];
                GenerateMethod(mapping);
            }
        }
 
        internal string? ReferenceMapping(TypeMapping mapping)
        {
            if (!mapping.IsSoap)
            {
                if (_generatedMethods[mapping] == null)
                {
                    _referencedMethods = EnsureArrayIndex(_referencedMethods!, _references);
                    _referencedMethods[_references++] = mapping;
                }
            }
            return (string?)_methodNames[mapping];
        }
 
        private static TypeMapping[] EnsureArrayIndex(TypeMapping[] a, int index)
        {
            if (a == null) return new TypeMapping[32];
            if (index < a.Length) return a;
            TypeMapping[] b = new TypeMapping[a.Length + 32];
            Array.Copy(a, b, index);
            return b;
        }
 
        internal void WriteQuotedCSharpString(string? value)
        {
            _raCodeGen.WriteQuotedCSharpString(value);
        }
 
        internal void GenerateHashtableGetBegin(string privateName, string publicName)
        {
            _writer.Write(typeof(Hashtable).FullName);
            _writer.Write(" ");
            _writer.Write(privateName);
            _writer.WriteLine(" = null;");
            _writer.Write("public override ");
            _writer.Write(typeof(Hashtable).FullName);
 
            _writer.Write(" ");
            _writer.Write(publicName);
            _writer.WriteLine(" {");
            _writer.Indent++;
 
            _writer.WriteLine("get {");
            _writer.Indent++;
 
            _writer.Write("if (");
            _writer.Write(privateName);
            _writer.WriteLine(" == null) {");
            _writer.Indent++;
 
            _writer.Write(typeof(Hashtable).FullName);
            _writer.Write(" _tmp = new ");
            _writer.Write(typeof(Hashtable).FullName);
            _writer.WriteLine("();");
        }
 
        internal void GenerateHashtableGetEnd(string privateName)
        {
            _writer.Write("if (");
            _writer.Write(privateName);
            _writer.Write(" == null) ");
            _writer.Write(privateName);
            _writer.WriteLine(" = _tmp;");
            _writer.Indent--;
            _writer.WriteLine("}");
 
            _writer.Write("return ");
            _writer.Write(privateName);
            _writer.WriteLine(";");
            _writer.Indent--;
            _writer.WriteLine("}");
 
            _writer.Indent--;
            _writer.WriteLine("}");
        }
        internal void GeneratePublicMethods(string privateName, string publicName, string?[] methods, XmlMapping[] xmlMappings)
        {
            GenerateHashtableGetBegin(privateName, publicName);
            if (methods != null && methods.Length != 0 && xmlMappings != null && xmlMappings.Length == methods.Length)
            {
                for (int i = 0; i < methods.Length; i++)
                {
                    if (methods[i] == null)
                        continue;
                    _writer.Write("_tmp[");
                    WriteQuotedCSharpString(xmlMappings[i].Key);
                    _writer.Write("] = ");
                    WriteQuotedCSharpString(methods[i]);
                    _writer.WriteLine(";");
                }
            }
            GenerateHashtableGetEnd(privateName);
        }
 
        internal void GenerateSupportedTypes(Type?[] types)
        {
            _writer.Write("public override ");
            _writer.Write(typeof(bool).FullName);
            _writer.Write(" CanSerialize(");
            _writer.Write(typeof(Type).FullName);
            _writer.WriteLine(" type) {");
            _writer.Indent++;
            Hashtable uniqueTypes = new Hashtable();
            for (int i = 0; i < types.Length; i++)
            {
                Type? type = types[i];
 
                if (type == null)
                    continue;
                if (!type.IsPublic && !type.IsNestedPublic)
                    continue;
                if (uniqueTypes[type] != null)
                    continue;
                if (DynamicAssemblies.IsTypeDynamic(type))
                    continue;
                if (type.IsGenericType || type.ContainsGenericParameters && DynamicAssemblies.IsTypeDynamic(type.GetGenericArguments()))
                    continue;
                uniqueTypes[type] = type;
                _writer.Write("if (type == typeof(");
                _writer.Write(CodeIdentifier.GetCSharpName(type));
                _writer.WriteLine(")) return true;");
            }
            _writer.WriteLine("return false;");
            _writer.Indent--;
            _writer.WriteLine("}");
        }
 
        internal string GenerateBaseSerializer(string baseSerializer, string readerClass, string writerClass, CodeIdentifiers classes)
        {
            baseSerializer = CodeIdentifier.MakeValid(baseSerializer);
            baseSerializer = classes.AddUnique(baseSerializer, baseSerializer);
 
            _writer.WriteLine();
            _writer.Write("public abstract class ");
            _writer.Write(CodeIdentifier.GetCSharpName(baseSerializer));
            _writer.Write(" : ");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializer).FullName);
            _writer.WriteLine(" {");
            _writer.Indent++;
 
            _writer.Write("protected override ");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName);
            _writer.WriteLine(" CreateReader() {");
            _writer.Indent++;
            _writer.Write("return new ");
            _writer.Write(readerClass);
            _writer.WriteLine("();");
            _writer.Indent--;
            _writer.WriteLine("}");
 
            _writer.Write("protected override ");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName);
            _writer.WriteLine(" CreateWriter() {");
            _writer.Indent++;
            _writer.Write("return new ");
            _writer.Write(writerClass);
            _writer.WriteLine("();");
            _writer.Indent--;
            _writer.WriteLine("}");
 
            _writer.Indent--;
            _writer.WriteLine("}");
 
            return baseSerializer;
        }
 
        internal string GenerateTypedSerializer(string? readMethod, string? writeMethod, XmlMapping mapping, CodeIdentifiers classes, string baseSerializer, string readerClass, string writerClass)
        {
            string serializerName = CodeIdentifier.MakeValid(Accessor.UnescapeName(mapping.Accessor.Mapping!.TypeDesc!.Name));
            serializerName = classes.AddUnique($"{serializerName}Serializer", mapping);
 
            _writer.WriteLine();
            _writer.Write("public sealed class ");
            _writer.Write(CodeIdentifier.GetCSharpName(serializerName));
            _writer.Write(" : ");
            _writer.Write(baseSerializer);
            _writer.WriteLine(" {");
            _writer.Indent++;
 
            _writer.WriteLine();
            _writer.Write("public override ");
            _writer.Write(typeof(bool).FullName);
            _writer.Write(" CanDeserialize(");
            _writer.Write(typeof(XmlReader).FullName);
            _writer.WriteLine(" xmlReader) {");
            _writer.Indent++;
 
            if (mapping.Accessor.Any)
            {
                _writer.WriteLine("return true;");
            }
            else
            {
                _writer.Write("return xmlReader.IsStartElement(");
                WriteQuotedCSharpString(mapping.Accessor.Name);
                _writer.Write(", ");
                WriteQuotedCSharpString(mapping.Accessor.Namespace);
                _writer.WriteLine(");");
            }
            _writer.Indent--;
            _writer.WriteLine("}");
 
            if (writeMethod != null)
            {
                _writer.WriteLine();
                _writer.Write("protected override void Serialize(object objectToSerialize, ");
                _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName);
                _writer.WriteLine(" writer) {");
                _writer.Indent++;
                _writer.Write("((");
                _writer.Write(writerClass);
                _writer.Write(")writer).");
                _writer.Write(writeMethod);
                _writer.Write("(");
                if (mapping is XmlMembersMapping)
                {
                    _writer.Write("(object[])");
                }
                _writer.WriteLine("objectToSerialize);");
                _writer.Indent--;
                _writer.WriteLine("}");
            }
            if (readMethod != null)
            {
                _writer.WriteLine();
                _writer.Write("protected override object Deserialize(");
                _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName);
                _writer.WriteLine(" reader) {");
                _writer.Indent++;
                _writer.Write("return ((");
                _writer.Write(readerClass);
                _writer.Write(")reader).");
                _writer.Write(readMethod);
                _writer.WriteLine("();");
                _writer.Indent--;
                _writer.WriteLine("}");
            }
            _writer.Indent--;
            _writer.WriteLine("}");
 
            return serializerName;
        }
 
        private void GenerateTypedSerializers(Hashtable serializers)
        {
            string privateName = "typedSerializers";
            GenerateHashtableGetBegin(privateName, "TypedSerializers");
 
            foreach (string key in serializers.Keys)
            {
                _writer.Write("_tmp.Add(");
                WriteQuotedCSharpString(key);
                _writer.Write(", new ");
                _writer.Write((string?)serializers[key]);
                _writer.WriteLine("());");
            }
            GenerateHashtableGetEnd("typedSerializers");
        }
 
        //GenerateGetSerializer(serializers, xmlMappings);
        private void GenerateGetSerializer(Hashtable serializers, XmlMapping[] xmlMappings)
        {
            _writer.Write("public override ");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializer).FullName);
            _writer.Write(" GetSerializer(");
            _writer.Write(typeof(Type).FullName);
            _writer.WriteLine(" type) {");
            _writer.Indent++;
 
            for (int i = 0; i < xmlMappings.Length; i++)
            {
                if (xmlMappings[i] is XmlTypeMapping)
                {
                    Type? type = xmlMappings[i].Accessor.Mapping!.TypeDesc!.Type;
                    if (type == null)
                        continue;
                    if (!type.IsPublic && !type.IsNestedPublic)
                        continue;
                    if (DynamicAssemblies.IsTypeDynamic(type))
                        continue;
                    if (type.IsGenericType || type.ContainsGenericParameters && DynamicAssemblies.IsTypeDynamic(type.GetGenericArguments()))
                        continue;
                    _writer.Write("if (type == typeof(");
                    _writer.Write(CodeIdentifier.GetCSharpName(type));
                    _writer.Write(")) return new ");
                    _writer.Write((string?)serializers[xmlMappings[i].Key!]);
                    _writer.WriteLine("();");
                }
            }
            _writer.WriteLine("return null;");
            _writer.Indent--;
            _writer.WriteLine("}");
        }
 
        internal void GenerateSerializerContract(XmlMapping[] xmlMappings, Type?[] types, string readerType, string?[] readMethods, string writerType, string?[] writerMethods, Hashtable serializers)
        {
            _writer.WriteLine();
            _writer.Write("public class XmlSerializerContract : global::");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializerImplementation).FullName);
            _writer.WriteLine(" {");
            _writer.Indent++;
 
            _writer.Write("public override global::");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName);
            _writer.Write(" Reader { get { return new ");
            _writer.Write(readerType);
            _writer.WriteLine("(); } }");
 
            _writer.Write("public override global::");
            _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName);
            _writer.Write(" Writer { get { return new ");
            _writer.Write(writerType);
            _writer.WriteLine("(); } }");
 
            GeneratePublicMethods(nameof(readMethods), "ReadMethods", readMethods, xmlMappings);
            GeneratePublicMethods("writeMethods", "WriteMethods", writerMethods, xmlMappings);
            GenerateTypedSerializers(serializers);
            GenerateSupportedTypes(types);
            GenerateGetSerializer(serializers, xmlMappings);
 
            _writer.Indent--;
            _writer.WriteLine("}");
        }
 
        internal static bool IsWildcard(SpecialMapping mapping)
        {
            if (mapping is SerializableMapping)
                return ((SerializableMapping)mapping).IsAny;
            return mapping.TypeDesc!.CanBeElementValue;
        }
    }
}