Esempio n. 1
0
        private void ImproveCodeDom(CodeNamespace codeNamespace)
        {
            var nonElementAttributes = new HashSet <string>(new[]
            {
                "System.Xml.Serialization.XmlAttributeAttribute",
                "System.Xml.Serialization.XmlIgnoreAttribute",
                "System.Xml.Serialization.XmlTextAttribute",
            });

            var nullValue = new CodePrimitiveExpression();

            codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
            codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));

            if (Options.UsingNamespaces != null)
            {
                foreach (var ns in Options.UsingNamespaces)
                {
                    codeNamespace.Imports.Add(new CodeNamespaceImport(ns));
                }
            }

            var neverBrowsableAttribute = new CodeAttributeDeclaration("System.ComponentModel.EditorBrowsable",
                                                                       new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("System.ComponentModel.EditorBrowsableState"), "Never")));

            var changedTypeNames = new Dictionary <string, string>();
            var newTypeNames     = new HashSet <string>();

            if (Options.UseXLinq)
            {
                changedTypeNames.Add("System.Xml.XmlNode", "System.Xml.Linq.XNode");
                changedTypeNames.Add("System.Xml.XmlElement", "System.Xml.Linq.XElement");
                changedTypeNames.Add("System.Xml.XmlAttribute", "System.Xml.Linq.XAttribute");
            }

            foreach (CodeTypeDeclaration codeType in codeNamespace.Types)
            {
                if (Options.TypeNameCapitalizer != null)
                {
                    var newName = Options.TypeNameCapitalizer.Capitalize(codeNamespace, codeType);
                    if (newName != codeType.Name)
                    {
                        SetAttributeOriginalName(codeType, codeType.GetOriginalName(), "System.Xml.Serialization.XmlTypeAttribute");
                        var newNameToAdd = newName;
                        var index        = 0;
                        while (!newTypeNames.Add(newNameToAdd))
                        {
                            index       += 1;
                            newNameToAdd = string.Format("{0}{1}", newName, index);
                        }
                        changedTypeNames.Add(codeType.Name, newNameToAdd);
                        codeType.Name = newNameToAdd;
                    }
                }

                var members = new Dictionary <string, CodeTypeMember>();
                foreach (CodeTypeMember member in codeType.Members)
                {
                    members[member.Name] = member;
                }

                if (Options.EnableDataBinding && codeType.IsClass && codeType.BaseTypes.Count == 0)
                {
                    codeType.BaseTypes.Add(typeof(object));
                    codeType.BaseTypes.Add(typeof(INotifyPropertyChanged));

                    codeType.Members.Add(new CodeMemberEvent()
                    {
                        Name = "PropertyChanged",
                        ImplementationTypes = { typeof(INotifyPropertyChanged) },
                        Attributes          = MemberAttributes.Public,
                        Type = new CodeTypeReference(typeof(PropertyChangedEventHandler))
                    });

                    codeType.Members.Add(new CodeMemberMethod()
                    {
                        Name       = "RaisePropertyChanged",
                        Attributes = MemberAttributes.Family | MemberAttributes.Final,
                        Parameters =
                        {
                            new CodeParameterDeclarationExpression(typeof(string), "propertyName")
                        },
                        Statements =
                        {
                            new CodeVariableDeclarationStatement(typeof(PropertyChangedEventHandler),                                                                                "propertyChanged",
                                                                 new CodeEventReferenceExpression(new CodeThisReferenceExpression(),                                                 "PropertyChanged")),
                            new CodeConditionStatement(new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("propertyChanged"),                                      CodeBinaryOperatorType.IdentityInequality,nullValue),
                                                       new CodeExpressionStatement(new CodeDelegateInvokeExpression(new CodeVariableReferenceExpression("propertyChanged"),
                                                                                                                    new CodeThisReferenceExpression(),
                                                                                                                    new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), new CodeArgumentReferenceExpression("propertyName")))))
                        }
                    });
                }

                if ((Options.AllTypesAreRoot || Options.AdditionalRootTypes.Contains(codeType.Name)) &&
                    !codeType.CustomAttributes.Cast <CodeAttributeDeclaration>().Any(x => x.Name == "System.Xml.Serialization.XmlRootAttribute"))
                {
                    var typeAttribute = codeType.CustomAttributes.Cast <CodeAttributeDeclaration>().FirstOrDefault(x => x.Name == "System.Xml.Serialization.XmlTypeAttribute");
                    var ns            = typeAttribute?.Arguments?.Cast <CodeAttributeArgument>().FirstOrDefault(x => x.Name == "Namespace");
                    if (ns != null)
                    {
                        var rootAttribute = new CodeAttributeDeclaration("System.Xml.Serialization.XmlRootAttribute",
                                                                         ns, new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression(false)));

                        codeType.CustomAttributes.Add(rootAttribute);
                    }
                }

                bool mixedContentDetected = Options.MixedContent && members.ContainsKey("textField") && members.ContainsKey("itemsField");

                var binaryDataTypes = new[] { "hexBinary", "base64Binary" };

                bool IsItemsChoiceType(CodeTypeReference reference) => reference.BaseType.StartsWith("ItemsChoiceType");

                var fieldNameToPropertyMapping = members.Values.OfType <CodeMemberProperty>().Select(x => new
                {
                    Property  = x,
                    FieldName = (x.GetStatements.OfType <CodeMethodReturnStatement>().SingleOrDefault()?.Expression as CodeFieldReferenceExpression)?.FieldName
                }).Where(x => x.FieldName != null).ToDictionary(x => x.FieldName, x => x.Property);

                var orderIndex = 0;
                foreach (CodeTypeMember member in members.Values)
                {
                    if (member is CodeMemberField)
                    {
                        CodeMemberField field = (CodeMemberField)member;

                        if (!fieldNameToPropertyMapping.TryGetValue(field.Name, out var backedProperty))
                        {
                            backedProperty = null;
                        }

                        bool isBinaryDataType = binaryDataTypes.Contains(backedProperty?.GetXmlDataType());
                        bool isItems          = IsItemsChoiceType(field.Type) || backedProperty?.HasChoiceIdentifierAttribute() == true;

                        if (mixedContentDetected)
                        {
                            switch (field.Name)
                            {
                            case "textField":
                                codeType.Members.Remove(member);
                                continue;

                            case "itemsField":
                                field.Type = new CodeTypeReference(typeof(object[]));
                                break;
                            }
                        }

                        if (Options.UseLists && field.Type.ArrayRank > 0 && !isBinaryDataType && !isItems)
                        {
                            CodeTypeReference type = new CodeTypeReference(typeof(List <>))
                            {
                                TypeArguments =
                                {
                                    field.Type.ArrayElementType
                                }
                            };

                            field.Type = type;
                        }

                        if (codeType.IsEnum && Options.EnumValueCapitalizer != null)
                        {
                            var newName = Options.EnumValueCapitalizer.Capitalize(codeNamespace, member);
                            if (newName != member.Name)
                            {
                                SetAttributeOriginalName(member, member.GetOriginalName(), "System.Xml.Serialization.XmlEnumAttribute");
                                member.Name = newName;
                            }
                        }
                    }

                    if (member is CodeMemberProperty)
                    {
                        CodeMemberProperty property = (CodeMemberProperty)member;

                        // Is this "*Specified" property part of a "propertyName" and "propertyNameSpecified" combination?
                        var isSpecifiedProperty = property.Name.EndsWith("Specified") && members.ContainsKey(property.Name.Substring(0, property.Name.Length - 9));

                        bool isBinaryDataType = binaryDataTypes.Contains(property.GetXmlDataType());
                        bool isItems          = IsItemsChoiceType(property.Type) || property.HasChoiceIdentifierAttribute();

                        if (mixedContentDetected)
                        {
                            switch (property.Name)
                            {
                            case "Text":
                                codeType.Members.Remove(member);
                                continue;

                            case "Items":
                                property.Type = new CodeTypeReference(typeof(object[]));
                                property.CustomAttributes.Add(new CodeAttributeDeclaration("System.Xml.Serialization.XmlTextAttribute", new CodeAttributeArgument {
                                    Name = "", Value = new CodeTypeOfExpression(new CodeTypeReference(typeof(string)))
                                }));
                                break;
                            }
                        }

                        string[] validXmlAttributeNames =
                        {
                            "System.Xml.Serialization.XmlEnumAttribute",
                            "System.Xml.Serialization.XmlTextAttribute",
                            "System.Xml.Serialization.XmlIgnoreAttribute",
                            "System.Xml.Serialization.XmlAttributeAttribute",
                            "System.Xml.Serialization.XmlElementAttribute",
                            "System.Xml.Serialization.XmlAnyAttributeAttribute",
                            "System.Xml.Serialization.XmlAnyElementAttribute",
                        };

                        var customAttributes     = property.CustomAttributes.Cast <CodeAttributeDeclaration>();
                        var customAttributeNames = new HashSet <string>(customAttributes.Select(x => x.Name));
                        if (!customAttributeNames.Overlaps(validXmlAttributeNames))
                        {
                            // is this an array item?
                            bool arrayItem = property
                                             .CustomAttributes.Cast <CodeAttributeDeclaration>()
                                             .Any(x => x.Name == "System.Xml.Serialization.XmlArrayItemAttribute");
                            if (arrayItem)
                            {
                                property.CustomAttributes.Add(new CodeAttributeDeclaration
                                {
                                    Name = "System.Xml.Serialization.XmlArrayAttribute"
                                });
                            }
                            else
                            {
                                // It is implied that this is an xml element. Explicitly add the corresponding attribute.
                                property.CustomAttributes.Add(new CodeAttributeDeclaration
                                {
                                    Name = "System.Xml.Serialization.XmlElementAttribute"
                                });
                            }
                        }

                        if (Options.UseLists && property.Type.ArrayRank > 0 && !isBinaryDataType && !isItems)
                        {
                            CodeTypeReference type = new CodeTypeReference(typeof(List <>))
                            {
                                TypeArguments =
                                {
                                    property.Type.ArrayElementType
                                }
                            };

                            property.Type = type;
                        }

                        bool capitalizeProperty;
                        if (!isSpecifiedProperty)
                        {
                            if (Options.PreserveOrder)
                            {
                                if (!property.CustomAttributes.Cast <CodeAttributeDeclaration>().Any(x => nonElementAttributes.Contains(x.Name)))
                                {
                                    var elementAttributes = property
                                                            .CustomAttributes.Cast <CodeAttributeDeclaration>()
                                                            .Where(x => x.Name == "System.Xml.Serialization.XmlElementAttribute" || x.Name == "System.Xml.Serialization.XmlArrayAttribute")
                                                            .ToList();
                                    if (elementAttributes.Count == 0)
                                    {
                                        // This should not happen (we implicitly add either XmlElementAttribute or XmlArrayAttribute above)
                                        throw new Exception("should not happen");
                                    }

                                    foreach (var elementAttribute in elementAttributes)
                                    {
                                        elementAttribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(orderIndex)));
                                    }

                                    orderIndex += 1;
                                }
                            }

                            if (Options.UseNullableTypes)
                            {
                                var            fieldName = GetFieldName(property.Name, "Field");
                                CodeTypeMember specified;
                                if (members.TryGetValue(property.Name + "Specified", out specified))
                                {
                                    var nullableProperty = new CodeMemberProperty
                                    {
                                        Name = property.Name,
                                        Type = new CodeTypeReference(typeof(Nullable <>))
                                        {
                                            TypeArguments = { property.Type.BaseType }
                                        },
                                        HasGet     = true,
                                        HasSet     = true,
                                        Attributes = MemberAttributes.Public | MemberAttributes.Final
                                    };

                                    nullableProperty.GetStatements.Add(
                                        new CodeConditionStatement(new CodeVariableReferenceExpression(fieldName + "Specified"),
                                                                   new CodeStatement[] { new CodeMethodReturnStatement(new CodeVariableReferenceExpression(fieldName)) },
                                                                   new CodeStatement[] { new CodeMethodReturnStatement(new CodePrimitiveExpression()) }
                                                                   ));

                                    nullableProperty.SetStatements.Add(
                                        new CodeConditionStatement(new CodeBinaryOperatorExpression(new CodePropertySetValueReferenceExpression(), CodeBinaryOperatorType.IdentityInequality, nullValue),
                                                                   new CodeStatement[]
                                    {
                                        new CodeAssignStatement(new CodeVariableReferenceExpression(fieldName + "Specified"),
                                                                new CodePrimitiveExpression(true)),
                                        new CodeAssignStatement(new CodeVariableReferenceExpression(fieldName),
                                                                new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), "Value")),
                                    },
                                                                   new CodeStatement[]
                                    {
                                        new CodeAssignStatement(
                                            new CodeVariableReferenceExpression(fieldName + "Specified"),
                                            new CodePrimitiveExpression(false)),
                                    }
                                                                   ));

                                    nullableProperty.CustomAttributes.Add(new CodeAttributeDeclaration
                                    {
                                        Name = "System.Xml.Serialization.XmlIgnoreAttribute"
                                    });

                                    codeType.Members.Add(nullableProperty);

                                    foreach (CodeAttributeDeclaration attribute in property.CustomAttributes)
                                    {
                                        if (attribute.Name == "System.Xml.Serialization.XmlAttributeAttribute")
                                        {
                                            var firstArgument = attribute.Arguments.Cast <CodeAttributeArgument>().FirstOrDefault();
                                            if (firstArgument == null || !string.IsNullOrEmpty(firstArgument.Name))
                                            {
                                                attribute.Arguments.Add(new CodeAttributeArgument
                                                {
                                                    Name  = "AttributeName",
                                                    Value = new CodePrimitiveExpression(property.Name)
                                                });
                                            }
                                        }
                                        else if (attribute.Name == "System.Xml.Serialization.XmlElementAttribute")
                                        {
                                            var firstArgument = attribute.Arguments.Cast <CodeAttributeArgument>().FirstOrDefault();
                                            if (firstArgument == null || !string.IsNullOrEmpty(firstArgument.Name))
                                            {
                                                attribute.Arguments.Add(new CodeAttributeArgument
                                                {
                                                    Name  = "ElementName",
                                                    Value = new CodePrimitiveExpression(property.Name)
                                                });
                                            }
                                        }
                                    }

                                    property.Name  = "_" + property.Name;
                                    specified.Name = "_" + specified.Name;

                                    if (Options.HideUnderlyingNullableProperties)
                                    {
                                        property.CustomAttributes.Add(neverBrowsableAttribute);
                                        specified.CustomAttributes.Add(neverBrowsableAttribute);
                                    }

                                    property = nullableProperty;
                                }
                            }

                            if (Options.EnableDataBinding)
                            {
                                property.SetStatements.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodePrimitiveExpression(property.Name)));
                            }

                            capitalizeProperty = Options.PropertyNameCapitalizer != null;
                        }
                        else if (!Options.UseNullableTypes)
                        {
                            if (Options.EnableDataBinding)
                            {
                                property.SetStatements.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodePrimitiveExpression(property.Name)));
                            }

                            capitalizeProperty = Options.PropertyNameCapitalizer != null;
                        }
                        else
                        {
                            capitalizeProperty = false;
                        }

                        if (capitalizeProperty)
                        {
                            var newName = Options.PropertyNameCapitalizer.Capitalize(codeNamespace, property);
                            if (newName != property.Name)
                            {
                                SetAttributeOriginalName(property, property.GetOriginalName(), "System.Xml.Serialization.XmlElementAttribute");
                                property.Name = newName;
                            }
                        }
                    }
                }
            }

            // Fixup changed type names
            if (changedTypeNames.Count != 0)
            {
                foreach (CodeTypeDeclaration codeType in codeNamespace.Types)
                {
                    if (codeType.IsEnum)
                    {
                        continue;
                    }

                    FixAttributeTypeReference(changedTypeNames, codeType);

                    foreach (CodeTypeMember member in codeType.Members)
                    {
                        var memberField = member as CodeMemberField;
                        if (memberField != null)
                        {
                            FixTypeReference(changedTypeNames, memberField.Type);
                            FixAttributeTypeReference(changedTypeNames, memberField);
                        }

                        var memberProperty = member as CodeMemberProperty;
                        if (memberProperty != null)
                        {
                            FixTypeReference(changedTypeNames, memberProperty.Type);
                            FixAttributeTypeReference(changedTypeNames, memberProperty);
                        }
                    }
                }
            }
        }