File: CodeDomFixup\ArrayOfXElementTypeHelper.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.
 
using System;
using Microsoft.CodeDom;
using Microsoft.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
 
namespace Microsoft.Tools.ServiceModel.Svcutil
{
    internal class ArrayOfXElementTypeHelper
    {
        private const string ArrayOfXElementTypeName = "ArrayOfXElement";
 
        private static CodeTypeDeclaration s_arrayOfXElements = null;
        private static CodeTypeReference s_arrayOfXElementRef = null;
 
        private List<string> _addedToCodeCompileUnit = new List<string>();
        private List<string> _needToAdd = new List<string>();
 
        private const string xelementType = "System.Xml.Linq.XElement";
        internal string SpecialNamespace = "";
 
        private static bool s_isInternal = false;
 
        internal ArrayOfXElementTypeHelper(bool isInternal, CodeCompileUnit cu)
        {
            ArrayOfXElementTypeHelper.s_isInternal = isInternal;
 
            //ArrayOfXElement will be in the global namespace
            CodeNamespace targetNamespace = cu.Namespaces.Count > 0 ? cu.Namespaces[0] : new CodeNamespace();
            SpecialNamespace = targetNamespace.Name;
            s_arrayOfXElements = CreateArrayOfXmlElementClass(targetNamespace);
            s_arrayOfXElementRef = new CodeTypeReference(GetUniqueClassName(targetNamespace));
        }
 
        internal static CodeTypeDeclaration ArrayOfXElements
        {
            get
            {
                return s_arrayOfXElements;
            }
        }
 
        internal static CodeTypeReference ArrayOfXElementRef
        {
            get
            {
                return s_arrayOfXElementRef;
            }
        }
 
        internal void CheckToAdd(string namespaceToAdd)
        {
            if (!_needToAdd.Contains(namespaceToAdd))
            {
                _needToAdd.Add(namespaceToAdd);
            }
        }
 
        internal static string GetUniqueClassName(CodeNamespace ns)
        {
            string uniqueName = ArrayOfXElementTypeName;
            Dictionary<string, CodeTypeDeclaration> nameTable = new Dictionary<string, CodeTypeDeclaration>();
 
            foreach (CodeTypeDeclaration type in ns.Types)
            {
                if (!nameTable.ContainsKey(type.Name))
                {
                    nameTable.Add(type.Name, type);
                }
            }
 
            int i = 0;
            while (nameTable.ContainsKey(uniqueName))
            {
                uniqueName = ArrayOfXElementTypeName + (++i).ToString(CultureInfo.InvariantCulture);
            }
            return uniqueName;
        }
 
        private static void AddGeneratedCodeAttribute(CodeTypeDeclaration codeType)
        {
            CodeAttributeDeclaration generatedCodeAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(GeneratedCodeAttribute)));
 
            AssemblyName assemblyName = typeof(ArrayOfXElementTypeHelper).GetTypeInfo().Assembly.GetName();
            generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Name)));
            generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Version.ToString())));
 
            codeType.CustomAttributes.Add(generatedCodeAttribute);
        }
 
        internal void AddToCompileUnit(CodeCompileUnit codeCompileUnit, string namespaceToAdd)
        {
            if (!_addedToCodeCompileUnit.Contains(namespaceToAdd) && _needToAdd.Contains(namespaceToAdd))
            {
                //if it is special namespace and there is only one namespace, add the special type to the first ns because that's when -n is used
                if (codeCompileUnit.Namespaces.Count > 0 && namespaceToAdd.Equals(SpecialNamespace))
                {
                    _needToAdd.Remove(namespaceToAdd);
                    namespaceToAdd = codeCompileUnit.Namespaces[0].Name;
                    _needToAdd.Add(namespaceToAdd);
                }
 
                foreach (CodeNamespace ns in codeCompileUnit.Namespaces)
                {
                    if (namespaceToAdd.Equals(ns.Name))
                    {
                        ns.Types.Add(ArrayOfXElements);
                        _addedToCodeCompileUnit.Add(namespaceToAdd);
                        break;
                    }
                }
            }
        }
 
        internal static CodeTypeReference CreateTypeReference(string typeName, params string[] typeArguments)
        {
            CodeTypeReference[] typeRefArgs = new CodeTypeReference[typeArguments.Length];
            for (int i = 0; i < typeArguments.Length; i++)
            {
                typeRefArgs[i] = new CodeTypeReference(typeArguments[i]);
            }
            return new CodeTypeReference(typeName, typeRefArgs);
        }
 
        private static CodeTypeDeclaration CreateArrayOfXmlElementClass(CodeNamespace ns)
        {
            CodeTypeDeclaration classToGen = new CodeTypeDeclaration(GetUniqueClassName(ns));
            classToGen.IsClass = true;
            classToGen.IsPartial = true;
            classToGen.TypeAttributes = s_isInternal ? TypeAttributes.NotPublic : TypeAttributes.Public;
 
            CodeAttributeDeclaration xmlSchemaProviderAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(Microsoft.Xml.Serialization.XmlSchemaProviderAttribute)),
                new CodeAttributeArgument(new CodePrimitiveExpression(null)),
                new CodeAttributeArgument("IsAny", new CodePrimitiveExpression(true)));
            classToGen.CustomAttributes.Add(xmlSchemaProviderAttribute);
 
            classToGen.BaseTypes.Add(new CodeTypeReference(typeof(Object)));
            classToGen.BaseTypes.Add(new CodeTypeReference(typeof(Microsoft.Xml.Serialization.IXmlSerializable)));
 
            CodeConstructor ctor = new CodeConstructor();
            ctor.Attributes = MemberAttributes.Public;
            classToGen.Members.Add(ctor);
 
            AddField(classToGen);
            AddProperty(classToGen);
            AddGetSchemaMethod(classToGen);
            AddWriteXml(classToGen);
            AddReadXml(classToGen);
            AddGeneratedCodeAttribute(classToGen);
 
            return classToGen;
        }
 
        private static void AddField(CodeTypeDeclaration classToGen)
        {
            CodeMemberField nodesField = new CodeMemberField();
            nodesField.Attributes = MemberAttributes.Private;
            nodesField.Name = "nodesList";
 
            CodeTypeReference listOfXElement = CreateTypeReference("System.Collections.Generic.List`1", xelementType);
            nodesField.Type = listOfXElement;
            nodesField.InitExpression = new CodeObjectCreateExpression(listOfXElement);
 
            classToGen.Members.Add(nodesField);
        }
 
        private static void AddProperty(CodeTypeDeclaration classToGen)
        {
            CodeMemberProperty nodesProperty = new CodeMemberProperty();
            nodesProperty.Attributes = MemberAttributes.Public;
            nodesProperty.Name = "Nodes";
 
            nodesProperty.HasGet = true;
            CodeTypeReference listOfXElement = CreateTypeReference("System.Collections.Generic.List`1", xelementType);
            nodesProperty.Type = listOfXElement;
 
            nodesProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "nodesList")));
 
            classToGen.Members.Add(nodesProperty);
        }
        private static void AddGetSchemaMethod(CodeTypeDeclaration classToGen)
        {
            CodeMemberMethod getSchemaMethod = new CodeMemberMethod();
            getSchemaMethod.Attributes = MemberAttributes.Public;
            getSchemaMethod.Name = "GetSchema";
            getSchemaMethod.ImplementationTypes.Add(classToGen.BaseTypes[1]);
            getSchemaMethod.ReturnType = new CodeTypeReference(typeof(Microsoft.Xml.Schema.XmlSchema));
 
            CodeThrowExceptionStatement throwException = new CodeThrowExceptionStatement(
                new CodeObjectCreateExpression(
                new CodeTypeReference(typeof(NotImplementedException)),
                new CodeExpression[] { }));
 
            getSchemaMethod.Statements.Add(throwException);
 
            classToGen.Members.Add(getSchemaMethod);
        }
        private static void AddWriteXml(CodeTypeDeclaration classToGen)
        {
            CodeMemberMethod writeXml = new CodeMemberMethod();
            writeXml.Name = "WriteXml";
            writeXml.Attributes = MemberAttributes.Public;
            writeXml.ImplementationTypes.Add(classToGen.BaseTypes[1]);
 
            writeXml.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(Microsoft.Xml.XmlWriter)), "writer"));
            CodeVariableDeclarationStatement enumeratorDec =
                new CodeVariableDeclarationStatement(
                    CreateTypeReference("System.Collections.Generic.IEnumerator`1", xelementType), "e",
                    new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("nodesList"), "GetEnumerator"));
 
            writeXml.Statements.Add(enumeratorDec);
 
            CodeVariableReferenceExpression eRef = new CodeVariableReferenceExpression("e");
            CodePropertyReferenceExpression eCurrent = new CodePropertyReferenceExpression(eRef, "Current");
            CodeCastExpression iXmlSerCast = new CodeCastExpression(new CodeTypeReference(typeof(Microsoft.Xml.Serialization.IXmlSerializable)), eCurrent);
            CodeMethodInvokeExpression codeWrite = new CodeMethodInvokeExpression(iXmlSerCast, "WriteXml", new CodeVariableReferenceExpression("writer"));
 
            CodeIterationStatement codeFor = new CodeIterationStatement();
            codeFor.TestExpression = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("e"), "MoveNext"));
            codeFor.Statements.Add(codeWrite);
            codeFor.IncrementStatement = new CodeSnippetStatement("");
            codeFor.InitStatement = new CodeSnippetStatement("");
 
            writeXml.Statements.Add(codeFor);
 
            classToGen.Members.Add(writeXml);
        }
 
        private static void AddReadXml(CodeTypeDeclaration classToGen)
        {
            CodeMemberMethod readXml = new CodeMemberMethod();
            readXml.Name = "ReadXml";
            readXml.Attributes = MemberAttributes.Public;
            readXml.ImplementationTypes.Add(classToGen.BaseTypes[1]);
 
            readXml.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(Microsoft.Xml.XmlReader)), "reader"));
 
            CodeVariableReferenceExpression reader = new CodeVariableReferenceExpression("reader");
            CodePropertyReferenceExpression readerNodeType = new CodePropertyReferenceExpression(reader, "NodeType");
            CodeTypeReferenceExpression xmlNodeType = new CodeTypeReferenceExpression(typeof(Microsoft.Xml.XmlNodeType));
            CodePropertyReferenceExpression xmlNodeTypeEndElement = new CodePropertyReferenceExpression(xmlNodeType, "EndElement");
            CodeBinaryOperatorExpression notEq = new CodeBinaryOperatorExpression(readerNodeType, CodeBinaryOperatorType.IdentityInequality, xmlNodeTypeEndElement);
 
            CodeIterationStatement codeFor = new CodeIterationStatement();
            codeFor.TestExpression = notEq;
            codeFor.IncrementStatement = new CodeSnippetStatement("");
            codeFor.InitStatement = new CodeSnippetStatement("");
 
            CodePropertyReferenceExpression xmlNodeTypeElement = new CodePropertyReferenceExpression(xmlNodeType, "Element");
            CodeBinaryOperatorExpression eQElement = new CodeBinaryOperatorExpression(readerNodeType, CodeBinaryOperatorType.ValueEquality, xmlNodeTypeElement);
 
            CodeVariableDeclarationStatement element =
                new CodeVariableDeclarationStatement(
                    new CodeTypeReference(xelementType), "elem",
                    new CodeObjectCreateExpression(new CodeTypeReference(xelementType), new CodePrimitiveExpression("default")));
 
            CodeCastExpression iXmlSerCast = new CodeCastExpression(new CodeTypeReference(typeof(Microsoft.Xml.Serialization.IXmlSerializable)), new CodeVariableReferenceExpression("elem"));
 
            CodeMethodInvokeExpression codeRead = new CodeMethodInvokeExpression(iXmlSerCast, "ReadXml", new CodeVariableReferenceExpression("reader"));
 
            CodeMethodInvokeExpression addNode =
                new CodeMethodInvokeExpression(
                    new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("Nodes"),
                        "Add"), new CodeVariableReferenceExpression("elem"));
 
            CodeConditionStatement codeIfElse = new CodeConditionStatement(eQElement);
            codeIfElse.TrueStatements.Add(element);
            codeIfElse.TrueStatements.Add(codeRead);
            codeIfElse.TrueStatements.Add(addNode);
 
            CodeMethodInvokeExpression skip = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("reader"), "Skip"));
            codeIfElse.FalseStatements.Add(skip);
 
            codeFor.Statements.Add(codeIfElse);
 
            readXml.Statements.Add(codeFor);
 
            classToGen.Members.Add(readXml);
        }
    }
}