File: FrameworkFork\Microsoft.Xml\Xml\Serialization\CodeExporter.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace Microsoft.Xml.Serialization
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Reflection;
    using Microsoft.CodeDom;
    using Microsoft.CodeDom.Compiler;
 
    /// <include file='doc\CodeExporter.uex' path='docs/doc[@for="CodeExporter"]/*' />
    ///<internalonly/>
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    public abstract class CodeExporter
    {
        private Hashtable _exportedMappings;
        private Hashtable _exportedClasses; // TypeMapping -> CodeTypeDeclaration
        private CodeNamespace _codeNamespace;
        private CodeCompileUnit _codeCompileUnit;
        private bool _rootExported;
        private TypeScope _scope;
        private CodeAttributeDeclarationCollection _includeMetadata = new CodeAttributeDeclarationCollection();
        private CodeGenerationOptions _options;
        private CodeDomProvider _codeProvider;
        private CodeAttributeDeclaration _generatedCodeAttribute;
 
        internal CodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeDomProvider codeProvider, CodeGenerationOptions options, Hashtable exportedMappings)
        {
            if (codeNamespace != null)
                CodeGenerator.ValidateIdentifiers(codeNamespace);
            _codeNamespace = codeNamespace;
            if (codeCompileUnit != null)
            {
                if (!codeCompileUnit.ReferencedAssemblies.Contains("System.dll"))
                    codeCompileUnit.ReferencedAssemblies.Add("System.dll");
                if (!codeCompileUnit.ReferencedAssemblies.Contains("System.Xml.dll"))
                    codeCompileUnit.ReferencedAssemblies.Add("System.Xml.dll");
            }
            _codeCompileUnit = codeCompileUnit;
            _options = options;
            _exportedMappings = exportedMappings;
            _codeProvider = codeProvider;
        }
 
        internal CodeCompileUnit CodeCompileUnit
        {
            get { return _codeCompileUnit; }
        }
 
        internal CodeNamespace CodeNamespace
        {
            get
            {
                if (_codeNamespace == null)
                    _codeNamespace = new CodeNamespace();
                return _codeNamespace;
            }
        }
        internal CodeDomProvider CodeProvider
        {
            get
            {
                if (_codeProvider == null)
                    _codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
                return _codeProvider;
            }
        }
 
        internal Hashtable ExportedClasses
        {
            get
            {
                if (_exportedClasses == null)
                    _exportedClasses = new Hashtable();
                return _exportedClasses;
            }
        }
 
        internal Hashtable ExportedMappings
        {
            get
            {
                if (_exportedMappings == null)
                    _exportedMappings = new Hashtable();
                return _exportedMappings;
            }
        }
 
        internal bool GenerateProperties
        {
            get { return (_options & CodeGenerationOptions.GenerateProperties) != 0; }
        }
 
        internal CodeAttributeDeclaration GeneratedCodeAttribute
        {
            get
            {
                if (_generatedCodeAttribute == null)
                {
                    CodeAttributeDeclaration decl = new CodeAttributeDeclaration(typeof(GeneratedCodeAttribute).FullName);
 
                    Assembly a = typeof(CodeExporter).GetTypeInfo().Assembly;
 
                    AssemblyName assemblyName = a.GetName();
                    decl.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Name)));
                    string version = GetProductVersion(a);
                    decl.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(version == null ? assemblyName.Version.ToString() : version)));
                    _generatedCodeAttribute = decl;
                }
                return _generatedCodeAttribute;
            }
        }
 
        internal static CodeAttributeDeclaration FindAttributeDeclaration(Type type, CodeAttributeDeclarationCollection metadata)
        {
            foreach (CodeAttributeDeclaration attribute in metadata)
            {
                if (attribute.Name == type.FullName || attribute.Name == type.Name)
                {
                    return attribute;
                }
            }
            return null;
        }
 
        private static string GetProductVersion(Assembly assembly)
        {
            foreach (var attrib in assembly.GetCustomAttributes())
            {
                var assemblyInfoVersion = attrib as AssemblyInformationalVersionAttribute;
                if (assemblyInfoVersion != null)
                {
                    return assemblyInfoVersion.InformationalVersion;
                }
            }
            return null;
        }
 
        /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.IncludeMetadata"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public CodeAttributeDeclarationCollection IncludeMetadata
        {
            get { return _includeMetadata; }
        }
 
        internal TypeScope Scope
        {
            get { return _scope; }
        }
 
        internal void CheckScope(TypeScope scope)
        {
            if (_scope == null)
            {
                _scope = scope;
            }
            else if (_scope != scope)
            {
                throw new InvalidOperationException(ResXml.XmlMappingsScopeMismatch);
            }
        }
 
        internal abstract void ExportDerivedStructs(StructMapping mapping);
        internal abstract void EnsureTypesExported(Accessor[] accessors, string ns);
 
        internal static void AddWarningComment(CodeCommentStatementCollection comments, string text)
        {
            Debug.Assert(comments != null);
            comments.Add(new CodeCommentStatement(string.Format(ResXml.XmlCodegenWarningDetails, text), false));
        }
 
        internal void ExportRoot(StructMapping mapping, Type includeType)
        {
            if (!_rootExported)
            {
                _rootExported = true;
                ExportDerivedStructs(mapping);
 
                for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
                {
                    if (!derived.ReferencedByElement && derived.IncludeInSchema && !derived.IsAnonymousType)
                    {
                        CodeAttributeDeclaration include = new CodeAttributeDeclaration(includeType.FullName);
                        include.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(derived.TypeDesc.FullName)));
                        _includeMetadata.Add(include);
                    }
                }
                Hashtable typesIncluded = new Hashtable();
                foreach (TypeMapping m in Scope.TypeMappings)
                {
                    if (m is ArrayMapping)
                    {
                        ArrayMapping arrayMapping = (ArrayMapping)m;
                        if (ShouldInclude(arrayMapping) && !typesIncluded.Contains(arrayMapping.TypeDesc.FullName))
                        {
                            CodeAttributeDeclaration include = new CodeAttributeDeclaration(includeType.FullName);
                            include.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(arrayMapping.TypeDesc.FullName)));
                            _includeMetadata.Add(include);
                            typesIncluded.Add(arrayMapping.TypeDesc.FullName, string.Empty);
                            EnsureTypesExported(arrayMapping.Elements, arrayMapping.Namespace);
                        }
                    }
                }
            }
        }
 
        private static bool ShouldInclude(ArrayMapping arrayMapping)
        {
            if (arrayMapping.ReferencedByElement)
                return false;
            if (arrayMapping.Next != null)
                return false;
            if (arrayMapping.Elements.Length == 1)
            {
                TypeKind kind = arrayMapping.Elements[0].Mapping.TypeDesc.Kind;
                if (kind == TypeKind.Node)
                    return false;
            }
 
            for (int i = 0; i < arrayMapping.Elements.Length; i++)
            {
                if (arrayMapping.Elements[i].Name != arrayMapping.Elements[i].Mapping.DefaultElementName)
                {
                    // in the case we need custom attributes to serialize an array instance, we cannot include arrau mapping without explicit reference.
                    return false;
                }
            }
            return true;
        }
 
        internal CodeTypeDeclaration ExportEnum(EnumMapping mapping, Type type)
        {
            CodeTypeDeclaration codeClass = new CodeTypeDeclaration(mapping.TypeDesc.Name);
 
            codeClass.Comments.Add(new CodeCommentStatement(ResXml.XmlRemarks, true));
            codeClass.IsEnum = true;
            if (mapping.IsFlags && mapping.Constants.Length > 31)
            {
                codeClass.BaseTypes.Add(new CodeTypeReference(typeof(long)));
            }
            codeClass.TypeAttributes |= TypeAttributes.Public;
            CodeNamespace.Types.Add(codeClass);
            for (int i = 0; i < mapping.Constants.Length; i++)
            {
                ExportConstant(codeClass, mapping.Constants[i], type, mapping.IsFlags, 1L << i);
            }
            if (mapping.IsFlags)
            {
                // Add [FlagsAttribute]
                CodeAttributeDeclaration flags = new CodeAttributeDeclaration(typeof(FlagsAttribute).FullName);
                codeClass.CustomAttributes.Add(flags);
            }
            CodeGenerator.ValidateIdentifiers(codeClass);
            return codeClass;
        }
 
        internal void AddTypeMetadata(CodeAttributeDeclarationCollection metadata, Type type, string defaultName, string name, string ns, bool includeInSchema)
        {
            CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(type.FullName);
            if (name == null || name.Length == 0)
            {
                attribute.Arguments.Add(new CodeAttributeArgument("AnonymousType", new CodePrimitiveExpression(true)));
            }
            else
            {
                if (defaultName != name)
                {
                    attribute.Arguments.Add(new CodeAttributeArgument("TypeName", new CodePrimitiveExpression(name)));
                }
            }
            if (ns != null && ns.Length != 0)
            {
                attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
            }
            if (!includeInSchema)
            {
                attribute.Arguments.Add(new CodeAttributeArgument("IncludeInSchema", new CodePrimitiveExpression(false)));
            }
            if (attribute.Arguments.Count > 0)
            {
                metadata.Add(attribute);
            }
        }
 
        internal static void AddIncludeMetadata(CodeAttributeDeclarationCollection metadata, StructMapping mapping, Type type)
        {
            if (mapping.IsAnonymousType)
                return;
            for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
            {
                CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(type.FullName);
                attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(derived.TypeDesc.FullName)));
                metadata.Add(attribute);
                AddIncludeMetadata(metadata, derived, type);
            }
        }
 
        internal static void ExportConstant(CodeTypeDeclaration codeClass, ConstantMapping constant, Type type, bool init, long enumValue)
        {
            CodeMemberField field = new CodeMemberField(typeof(int).FullName, constant.Name);
            field.Comments.Add(new CodeCommentStatement(ResXml.XmlRemarks, true));
            if (init)
                field.InitExpression = new CodePrimitiveExpression(enumValue);
            codeClass.Members.Add(field);
            if (constant.XmlName != constant.Name)
            {
                CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(type.FullName);
                attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(constant.XmlName)));
                field.CustomAttributes.Add(attribute);
            }
        }
 
        internal static object PromoteType(Type type, object value)
        {
            if (type == typeof(sbyte))
            {
                return ((IConvertible)value).ToInt16(null);
            }
            else if (type == typeof(UInt16))
            {
                return ((IConvertible)value).ToInt32(null);
            }
            else if (type == typeof(UInt32))
            {
                return ((IConvertible)value).ToInt64(null);
            }
            else if (type == typeof(UInt64))
            {
                return ((IConvertible)value).ToDecimal(null);
            }
            else
            {
                return value;
            }
        }
 
        internal CodeMemberProperty CreatePropertyDeclaration(CodeMemberField field, string name, string typeName)
        {
            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Type = new CodeTypeReference(typeName);
            prop.Name = name;
            if(name.Equals("System"))
            {
                prop.Name = "SystemMember";
            }
            prop.Attributes = (prop.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
 
            //add get
            CodeMethodReturnStatement ret = new CodeMethodReturnStatement();
            ret.Expression = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
            prop.GetStatements.Add(ret);
 
            CodeAssignStatement propertySet = new CodeAssignStatement();
            CodeExpression left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
            CodeExpression right = new CodePropertySetValueReferenceExpression();
            propertySet.Left = left;
            propertySet.Right = right;
 
            if (EnableDataBinding)
            {
                prop.SetStatements.Add(propertySet);
                prop.SetStatements.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), RaisePropertyChangedEventMethod.Name, new CodePrimitiveExpression(name)));
            }
            else
                prop.SetStatements.Add(propertySet);
 
            return prop;
        }
 
        internal static string MakeFieldName(string name)
        {
            return CodeIdentifier.MakeCamel(name) + "Field";
        }
 
        internal void AddPropertyChangedNotifier(CodeTypeDeclaration codeClass)
        {
            if (EnableDataBinding && codeClass != null)
            {
                if (codeClass.BaseTypes.Count == 0)
                {
                    codeClass.BaseTypes.Add(typeof(object));
                }
                codeClass.BaseTypes.Add(new CodeTypeReference(typeof(System.ComponentModel.INotifyPropertyChanged)));
                codeClass.Members.Add(PropertyChangedEvent);
                codeClass.Members.Add(RaisePropertyChangedEventMethod);
            }
        }
 
        private bool EnableDataBinding
        {
            get { return (_options & CodeGenerationOptions.EnableDataBinding) != 0; }
        }
 
        internal static CodeMemberMethod RaisePropertyChangedEventMethod
        {
            get
            {
                CodeMemberMethod raisePropertyChangedEventMethod = new CodeMemberMethod();
                raisePropertyChangedEventMethod.Name = "RaisePropertyChanged";
                raisePropertyChangedEventMethod.Attributes = MemberAttributes.Family | MemberAttributes.Final;
                CodeArgumentReferenceExpression propertyName = new CodeArgumentReferenceExpression("propertyName");
                raisePropertyChangedEventMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), propertyName.ParameterName));
                CodeVariableReferenceExpression propertyChanged = new CodeVariableReferenceExpression("propertyChanged");
                raisePropertyChangedEventMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(PropertyChangedEventHandler), propertyChanged.VariableName, new CodeEventReferenceExpression(new CodeThisReferenceExpression(), PropertyChangedEvent.Name)));
                CodeConditionStatement ifStatement = new CodeConditionStatement(new CodeBinaryOperatorExpression(propertyChanged, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)));
                raisePropertyChangedEventMethod.Statements.Add(ifStatement);
                ifStatement.TrueStatements.Add(new CodeDelegateInvokeExpression(propertyChanged, new CodeThisReferenceExpression(), new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), propertyName)));
                return raisePropertyChangedEventMethod;
            }
        }
 
        internal static CodeMemberEvent PropertyChangedEvent
        {
            get
            {
                CodeMemberEvent propertyChangedEvent = new CodeMemberEvent();
                propertyChangedEvent.Attributes = MemberAttributes.Public;
                propertyChangedEvent.Name = "PropertyChanged";
                propertyChangedEvent.Type = new CodeTypeReference(typeof(PropertyChangedEventHandler));
                propertyChangedEvent.ImplementationTypes.Add(typeof(System.ComponentModel.INotifyPropertyChanged));
                return propertyChangedEvent;
            }
        }
    }
}