From 4a9afce24156f2a224bdcea637b24b23c1dcf007 Mon Sep 17 00:00:00 2001 From: Lewis Smith Date: Tue, 7 Jun 2022 02:36:50 +0100 Subject: [PATCH 1/4] Fixed NRT issues and tidied code. --- XmlSchemaClassGenerator.Tests/XmlTests.cs | 2 +- XmlSchemaClassGenerator/CodeUtilities.cs | 388 +++------ XmlSchemaClassGenerator/ModelBuilder.cs | 2 +- XmlSchemaClassGenerator/RestrictionModel.cs | 262 ++---- XmlSchemaClassGenerator/TypeModel.cs | 872 ++++++++------------ 5 files changed, 512 insertions(+), 1014 deletions(-) diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index 0216e8d5..33549254 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -2399,7 +2399,7 @@ void assertNullable(string typename, bool nullable) Assert.Equal(nullable, hasMaybeNullAttribute); } assertNullable("Test.ElementReferenceNullable", true); - assertNullable("Test.ElementReferenceList", true); + assertNullable("Test.ElementReferenceList", false); assertNullable("Test.ElementReferenceNonNullable", false); assertNullable("Test.AttributeReferenceNullable", true); assertNullable("Test.AttributeReferenceNonNullable", false); diff --git a/XmlSchemaClassGenerator/CodeUtilities.cs b/XmlSchemaClassGenerator/CodeUtilities.cs index 6f87d805..adbda9cc 100644 --- a/XmlSchemaClassGenerator/CodeUtilities.cs +++ b/XmlSchemaClassGenerator/CodeUtilities.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; @@ -12,165 +13,88 @@ namespace XmlSchemaClassGenerator public static class CodeUtilities { // Match non-letter followed by letter - static readonly Regex PascalCaseRegex = new(@"[^\p{L}]\p{L}", RegexOptions.Compiled); + private static readonly Regex PascalCaseRegex = new(@"[^\p{L}]\p{L}", RegexOptions.Compiled); // Uppercases first letter and all letters following non-letters. // Examples: testcase -> Testcase, html5element -> Html5Element, test_case -> Test_Case - public static string ToPascalCase(this string s) - { - if (string.IsNullOrEmpty(s)) { return s; } - return char.ToUpperInvariant(s[0]) - + PascalCaseRegex.Replace(s.Substring(1), m => m.Value[0] + char.ToUpperInvariant(m.Value[1]).ToString()); - } + public static string ToPascalCase(this string s) => string.IsNullOrEmpty(s) ? s + : char.ToUpperInvariant(s[0]) + PascalCaseRegex.Replace(s.Substring(1), m => m.Value[0] + char.ToUpperInvariant(m.Value[1]).ToString()); - public static string ToCamelCase(this string s) - { - if (string.IsNullOrEmpty(s)) { return s; } - return char.ToLowerInvariant(s[0]) + s.Substring(1); - } + public static string ToCamelCase(this string s) => string.IsNullOrEmpty(s) ? s + : char.ToLowerInvariant(s[0]) + s.Substring(1); public static string ToBackingField(this string propertyName, string privateFieldPrefix) - { - return string.Concat(privateFieldPrefix, propertyName.ToCamelCase()); - } + => string.Concat(privateFieldPrefix, propertyName.ToCamelCase()); - public static bool? IsDataTypeAttributeAllowed(this XmlSchemaDatatype type) + public static bool? IsDataTypeAttributeAllowed(this XmlSchemaDatatype type) => type.TypeCode switch { - bool? result = type.TypeCode switch - { - XmlTypeCode.AnyAtomicType => false,// union - XmlTypeCode.DateTime or XmlTypeCode.Time or XmlTypeCode.Date or XmlTypeCode.Base64Binary or XmlTypeCode.HexBinary => true, - _ => false, - }; - - return result; - } + XmlTypeCode.AnyAtomicType => false,// union + XmlTypeCode.DateTime or XmlTypeCode.Time or XmlTypeCode.Date or XmlTypeCode.Base64Binary or XmlTypeCode.HexBinary => true, + _ => false, + }; - private static Type GetIntegerDerivedType(XmlSchemaDatatype type, GeneratorConfiguration configuration, IEnumerable restrictions) + private static Type GetIntegerDerivedType(XmlSchemaDatatype xml, GeneratorConfiguration configuration, IEnumerable restrictions) { if (configuration.IntegerDataType != null && !configuration.UseIntegerDataTypeAsFallback) return configuration.IntegerDataType; - var xmlTypeCode = type.TypeCode; - - Type result = null; - - var maxInclusive = restrictions.OfType().SingleOrDefault(); - var minInclusive = restrictions.OfType().SingleOrDefault(); - - decimal? maxInclusiveValue = null; - if (maxInclusive is null && xmlTypeCode == XmlTypeCode.NegativeInteger) + decimal? maxInclusive = (restrictions.OfType().SingleOrDefault(), xml.TypeCode) switch { - maxInclusiveValue = -1; - } - else if (maxInclusive is null && xmlTypeCode == XmlTypeCode.NonPositiveInteger) - { - maxInclusiveValue = 0; - } - else if (maxInclusive != null && decimal.TryParse(maxInclusive.Value, out decimal value)) - { - maxInclusiveValue = value; - } + (null, XmlTypeCode.NegativeInteger) => -1, + (null, XmlTypeCode.NonPositiveInteger) => 0, + ({ Value: var str }, _) when decimal.TryParse(str, out decimal value) => value, + _ => null, + }; - decimal? minInclusiveValue = null; - if (minInclusive is null && xmlTypeCode == XmlTypeCode.PositiveInteger) - { - minInclusiveValue = 1; - } - else if (minInclusive is null && xmlTypeCode == XmlTypeCode.NonNegativeInteger) + decimal? minInclusive = (restrictions.OfType().SingleOrDefault(), xml.TypeCode) switch { - minInclusiveValue = 0; - } - else if (minInclusive != null && decimal.TryParse(minInclusive.Value, out decimal value)) - { - minInclusiveValue = value; - } + (null, XmlTypeCode.PositiveInteger) => 1, + (null, XmlTypeCode.NonNegativeInteger) => 0, + ({ Value: var str }, _) when decimal.TryParse(str, out decimal value) => value, + _ => null, + }; // If either value is null, then that value is either unbounded or too large to fit in any numeric type. - if (minInclusiveValue != null && maxInclusiveValue != null) { - if (minInclusiveValue >= byte.MinValue && maxInclusiveValue <= byte.MaxValue) - result = typeof(byte); - else if (minInclusiveValue >= sbyte.MinValue && maxInclusiveValue <= sbyte.MaxValue) - result = typeof(sbyte); - else if (minInclusiveValue >= ushort.MinValue && maxInclusiveValue <= ushort.MaxValue) - result = typeof(ushort); - else if (minInclusiveValue >= short.MinValue && maxInclusiveValue <= short.MaxValue) - result = typeof(short); - else if (minInclusiveValue >= uint.MinValue && maxInclusiveValue <= uint.MaxValue) - result = typeof(uint); - else if (minInclusiveValue >= int.MinValue && maxInclusiveValue <= int.MaxValue) - result = typeof(int); - else if (minInclusiveValue >= ulong.MinValue && maxInclusiveValue <= ulong.MaxValue) - result = typeof(ulong); - else if (minInclusiveValue >= long.MinValue && maxInclusiveValue <= long.MaxValue) - result = typeof(long); - else // If it didn't fit in a decimal, we could not have gotten here. - result = typeof(decimal); - - return result; - } + return FromMinMax() ?? FromDigitRestriction(restrictions.OfType().SingleOrDefault()) ?? FromFallback(); - if (restrictions.SingleOrDefault(r => r is TotalDigitsRestrictionModel) is not TotalDigitsRestrictionModel totalDigits - || ((xmlTypeCode == XmlTypeCode.PositiveInteger - || xmlTypeCode == XmlTypeCode.NonNegativeInteger) && totalDigits.Value >= 30) - || ((xmlTypeCode == XmlTypeCode.Integer - || xmlTypeCode == XmlTypeCode.NegativeInteger - || xmlTypeCode == XmlTypeCode.NonPositiveInteger) && totalDigits.Value >= 29)) + Type FromMinMax() => (minInclusive, maxInclusive) switch { - if (configuration.UseIntegerDataTypeAsFallback && configuration.IntegerDataType != null) - return configuration.IntegerDataType; - return typeof(string); - } + (null, _) => null, + (_, null) => null, + ( >= byte.MinValue, <= byte.MaxValue) => typeof(byte), + ( >= sbyte.MinValue, <= sbyte.MaxValue) => typeof(sbyte), + ( >= ushort.MinValue, <= ushort.MaxValue) => typeof(ushort), + ( >= short.MinValue, <= short.MaxValue) => typeof(short), + ( >= uint.MinValue, <= uint.MaxValue) => typeof(uint), + ( >= int.MinValue, <= int.MaxValue) => typeof(int), + ( >= ulong.MinValue, <= ulong.MaxValue) => typeof(ulong), + ( >= long.MinValue, <= long.MaxValue) => typeof(long), + _ => typeof(decimal), + }; - switch (xmlTypeCode) + Type FromDigitRestriction(TotalDigitsRestrictionModel totalDigits) => xml.TypeCode switch { - case XmlTypeCode.PositiveInteger: - case XmlTypeCode.NonNegativeInteger: - switch (totalDigits.Value) - { - case int n when (n < 3): - result = typeof(byte); - break; - case int n when (n < 5): - result = typeof(ushort); - break; - case int n when (n < 10): - result = typeof(uint); - break; - case int n when (n < 20): - result = typeof(ulong); - break; - case int n when (n < 30): - result = typeof(decimal); - break; - } - - break; - - case XmlTypeCode.Integer: - case XmlTypeCode.NegativeInteger: - case XmlTypeCode.NonPositiveInteger: - switch (totalDigits.Value) - { - case int n when (n < 3): - result = typeof(sbyte); - break; - case int n when (n < 5): - result = typeof(short); - break; - case int n when (n < 10): - result = typeof(int); - break; - case int n when (n < 19): - result = typeof(long); - break; - case int n when (n < 29): - result = typeof(decimal); - break; - } - break; - } + XmlTypeCode.PositiveInteger or XmlTypeCode.NonNegativeInteger => totalDigits?.Value switch + { + < 3 => typeof(byte), + < 5 => typeof(ushort), + < 10 => typeof(uint), + < 20 => typeof(ulong), + < 30 => typeof(decimal), + _ => null + }, + XmlTypeCode.Integer or XmlTypeCode.NegativeInteger or XmlTypeCode.NonPositiveInteger => totalDigits?.Value switch + { + < 3 => typeof(sbyte), + < 5 => typeof(short), + < 10 => typeof(int), + < 19 => typeof(long), + < 29 => typeof(decimal), + _ => null + }, + _ => null, + }; - return result; + Type FromFallback() => configuration.UseIntegerDataTypeAsFallback && configuration.IntegerDataType != null ? configuration.IntegerDataType : typeof(string); } public static Type GetEffectiveType(this XmlSchemaDatatype type, GeneratorConfiguration configuration, IEnumerable restrictions, bool attribute = false) @@ -217,21 +141,14 @@ public static XmlQualifiedName GetQualifiedName(this TypeModel typeModel) XmlQualifiedName qualifiedName; if (typeModel is not SimpleModel simpleTypeModel) { - if (typeModel.IsAnonymous) - { - qualifiedName = typeModel.XmlSchemaName; - } - else - { - qualifiedName = typeModel.XmlSchemaType.GetQualifiedName(); - } + qualifiedName = typeModel.IsAnonymous ? typeModel.XmlSchemaName + : typeModel.XmlSchemaType.GetQualifiedName(); } else { qualifiedName = simpleTypeModel.XmlSchemaType.GetQualifiedName(); var xmlSchemaType = simpleTypeModel.XmlSchemaType; - while (qualifiedName.Namespace != XmlSchema.Namespace && - xmlSchemaType.BaseXmlSchemaType != null) + while (qualifiedName.Namespace != XmlSchema.Namespace && xmlSchemaType.BaseXmlSchemaType != null) { xmlSchemaType = xmlSchemaType.BaseXmlSchemaType; qualifiedName = xmlSchemaType.GetQualifiedName(); @@ -243,13 +160,9 @@ public static XmlQualifiedName GetQualifiedName(this TypeModel typeModel) public static string GetUniqueTypeName(this NamespaceModel model, string name) { var n = name; - var i = 2; - while (model.Types.ContainsKey(n) && model.Types[n] is not SimpleModel) - { + for (var i = 2; model.Types.ContainsKey(n) && model.Types[n] is not SimpleModel; i++) n = name + i; - i++; - } return n; } @@ -259,90 +172,49 @@ public static string GetUniqueFieldName(this TypeModel typeModel, PropertyModel var classModel = typeModel as ClassModel; var propBackingFieldName = propertyModel.Name.ToBackingField(classModel?.Configuration.PrivateMemberPrefix); - if (CSharpKeywords.Contains(propBackingFieldName.ToLower())) + if (!IsValidIdentifier(propBackingFieldName.ToLower())) propBackingFieldName = "@" + propBackingFieldName; if (classModel == null) - { return propBackingFieldName; - } var i = 0; foreach (var prop in classModel.Properties) { if (propertyModel == prop) { - i += 1; + i++; break; } var backingFieldName = prop.Name.ToBackingField(classModel.Configuration.PrivateMemberPrefix); if (backingFieldName == propBackingFieldName) - { - i += 1; - } + i++; } - if (i <= 1) - { - return propBackingFieldName; - } - - return string.Format("{0}{1}", propBackingFieldName, i); + return i <= 1 ? propBackingFieldName : $"{propBackingFieldName}{i}"; } public static string GetUniquePropertyName(this TypeModel tm, string name) { - if (tm is ClassModel cls) - { - var i = 0; - var n = name; - var baseClasses = cls.AllBaseClasses.ToList(); - var props = cls.Properties.ToList(); - - while (baseClasses.SelectMany(b => b.Properties) - .Concat(props) - .Any(p => p.Name == n)) - { - n = name + (++i); - } + if (tm is not ClassModel cls) return name; - return n; - } + var i = 0; + var n = name; + var baseProps = cls.AllBaseClasses.SelectMany(b => b.Properties).ToList(); + var props = cls.Properties.ToList(); + + while (baseProps.Concat(props).Any(p => p.Name == n)) + n = name + (++i); - return name; + return n; } - static readonly Regex NormalizeNewlinesRegex = new (@"(^|[^\r])\n", RegexOptions.Compiled); + private static readonly Regex NormalizeNewlinesRegex = new(@"(^|[^\r])\n", RegexOptions.Compiled); - internal static string NormalizeNewlines(string text) - { - return NormalizeNewlinesRegex.Replace(text, "$1\r\n"); - } + internal static string NormalizeNewlines(string text) => NormalizeNewlinesRegex.Replace(text, "$1\r\n"); - static readonly List CSharpKeywords = new() - { - "abstract", "as", "base", "bool", - "break", "byte", "case", "catch", - "char", "checked", "class", "const", - "continue", "decimal", "default", "delegate", - "do", "double", "else", "enum", - "event", " explicit", "extern", "false", - "finally", "fixed", "float", "for", - "foreach", "goto", "if", "implicit", - "in", "int", "interface", "internal", - "is", "lock", "long", "namespace", - "new", "null", "object", "operator", - "out", "override", "params", "private", - "protected", "public", "readonly", "ref", - "return", "sbyte", "sealed", "short", - "sizeof", "stackalloc", "static", "string", - "struct", "switch", "this", "throw", - "true", "try", "typeof", "uint", - "ulong", "unchecked", "unsafe", "ushort", - "using", "using static", "virtual", "void", - "volatile", "while" - }; + private static readonly Predicate IsValidIdentifier = new Microsoft.CSharp.CSharpCodeProvider().IsValidIdentifier; internal static Uri CreateUri(string uri) => string.IsNullOrEmpty(uri) ? null : new Uri(uri); @@ -350,9 +222,7 @@ public static KeyValuePair ParseNamespace(string nsArg, st { var parts = nsArg.Split(new[] { '=' }, 2); if (parts.Length != 2) - { throw new ArgumentException("XML and C# namespaces should be separated by '='. You entered: " + nsArg); - } var xmlNs = parts[0]; var netNs = parts[1]; @@ -360,13 +230,12 @@ public static KeyValuePair ParseNamespace(string nsArg, st var source = parts2.Length == 2 ? new Uri(parts2[1], UriKind.RelativeOrAbsolute) : null; xmlNs = parts2[0]; if (!string.IsNullOrEmpty(namespacePrefix)) - { netNs = namespacePrefix + "." + netNs; - } + return new KeyValuePair(new NamespaceKey(source, xmlNs), netNs); } - public static readonly ImmutableList<(string Namespace, Func Condition)> UsingNamespaces = ImmutableList.Create<(string Namespace, Func Condition)>( + public static readonly ImmutableList<(string Namespace, Func Condition)> UsingNamespaces = ImmutableList.Create<(string, Func)>( ("System", c => c.CompactTypeNames), ("System.CodeDom.Compiler", c => c.CompactTypeNames), ("System.Collections.Generic", c => c.CompactTypeNames), @@ -374,26 +243,25 @@ public static KeyValuePair ParseNamespace(string nsArg, st ("System.ComponentModel", c => c.CompactTypeNames), ("System.ComponentModel.DataAnnotations", c => c.CompactTypeNames && (c.DataAnnotationMode != DataAnnotationMode.None || c.EntityFramework)), ("System.Diagnostics", c => c.CompactTypeNames && c.GenerateDebuggerStepThroughAttribute), + ("System.Diagnostics.CodeAnalysis", c => c.CompactTypeNames && c.EnableNullableReferenceAttributes), ("System.Linq", c => c.EnableDataBinding), ("System.Xml", c => c.CompactTypeNames), ("System.Xml.Schema", c => c.CompactTypeNames), ("System.Xml.Serialization", c => c.CompactTypeNames) ); - public static bool IsUsingNamespace(Type t, GeneratorConfiguration conf) => UsingNamespaces.Any(n => n.Namespace == t.Namespace && n.Condition(conf)); - - public static bool IsUsingNamespace(string namespaceName, GeneratorConfiguration conf) => UsingNamespaces.Any(n => n.Namespace == namespaceName && n.Condition(conf)); + public static bool IsUsingNamespace(string namespaceName, GeneratorConfiguration conf) + => UsingNamespaces.Any(n => n.Namespace == namespaceName && n.Condition(conf)); - public static CodeTypeReference CreateTypeReference(Type t, GeneratorConfiguration conf) + public static CodeTypeReference CreateTypeReference(Type type, GeneratorConfiguration conf) { - if (IsUsingNamespace(t, conf)) + if (IsUsingNamespace(type.Namespace, conf)) { - var name = t.Name; - var typeRef = new CodeTypeReference(name, conf.CodeTypeReferenceOptions); + var typeRef = new CodeTypeReference(type.Name, conf.CodeTypeReferenceOptions); - if (t.IsConstructedGenericType) + if (type.IsConstructedGenericType) { - var typeArgs = t.GenericTypeArguments.Select(a => CreateTypeReference(a, conf)).ToArray(); + var typeArgs = type.GenericTypeArguments.Select(a => CreateTypeReference(a, conf)).ToArray(); typeRef.TypeArguments.AddRange(typeArgs); } @@ -401,31 +269,20 @@ public static CodeTypeReference CreateTypeReference(Type t, GeneratorConfigurati } else { - var typeRef = new CodeTypeReference(t, conf.CodeTypeReferenceOptions); + var typeRef = new CodeTypeReference(type, conf.CodeTypeReferenceOptions); - foreach (var typeArg in typeRef.TypeArguments) - { - if (typeArg is CodeTypeReference typeArgRef) - { - typeArgRef.Options = conf.CodeTypeReferenceOptions; - } - } + foreach (var typeArgRef in typeRef.TypeArguments.OfType()) + typeArgRef.Options = conf.CodeTypeReferenceOptions; return typeRef; } - } - public static CodeTypeReference CreateTypeReference(string namespaceName, string typeName, GeneratorConfiguration conf) + public static CodeTypeReference CreateTypeReference(TypeInfo type, GeneratorConfiguration conf) { - if (IsUsingNamespace(namespaceName, conf)) - { - var typeRef = new CodeTypeReference(typeName, conf.CodeTypeReferenceOptions); - - return typeRef; - } - else - return new CodeTypeReference($"{namespaceName}.{typeName}", conf.CodeTypeReferenceOptions); + return IsUsingNamespace(type.Namespace, conf) + ? new CodeTypeReference(type.Name, conf.CodeTypeReferenceOptions) + : new CodeTypeReference($"{type.Namespace}.{type.Name}", conf.CodeTypeReferenceOptions); } /// @@ -433,21 +290,42 @@ public static CodeTypeReference CreateTypeReference(string namespaceName, string /// and https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlattributeattribute#remarks /// public static bool IsXmlLangOrSpace(XmlQualifiedName name) - { - return name != null && name.Namespace == "http://www.w3.org/XML/1998/namespace" - && (name.Name == "lang" || name.Name == "space"); - } + => name?.Namespace == "http://www.w3.org/XML/1998/namespace" && (name.Name == "lang" || name.Name == "space"); - internal static XmlQualifiedName GetQualifiedName(this XmlSchemaObject obj) + internal static XmlQualifiedName GetQualifiedName(this XmlSchemaObject obj) => obj switch { - var n = obj switch - { - XmlSchemaAttribute attr => attr.QualifiedName, - XmlSchemaAttributeGroup attrGroup => attrGroup.QualifiedName, - _ => null - }; + XmlSchemaAttribute attr => attr.QualifiedName, + XmlSchemaAttributeGroup attrGroup => attrGroup.QualifiedName, + _ => null + }; + } - return n; - } + public readonly record struct TypeInfo(string Namespace, string Name); + + /// + /// For attributes which can't be referenced by typeof() + /// + internal static class Attributes + { + private const string DataAnnotations = "System.ComponentModel.DataAnnotations"; + private const string CodeAnalysis = "System.Diagnostics.CodeAnalysis"; + + private static TypeInfo Make(string @namespace, [CallerMemberName] string name = null) + => new(@namespace, name + "Attribute"); + + public static TypeInfo Required { get; } = Make(DataAnnotations); + public static TypeInfo Key { get; } = Make(DataAnnotations); + public static TypeInfo Range { get; } = Make(DataAnnotations); + public static TypeInfo MinLength { get; } = Make(DataAnnotations); + public static TypeInfo MaxLength { get; } = Make(DataAnnotations); + public static TypeInfo StringLength { get; } = Make(DataAnnotations); + public static TypeInfo RegularExpression { get; } = Make(DataAnnotations); + public static TypeInfo NotMapped { get; } = Make($"{DataAnnotations}.Schema"); + + public static TypeInfo AllowNull { get; } = Make(CodeAnalysis); + public static TypeInfo MaybeNull { get; } = Make(CodeAnalysis); } -} \ No newline at end of file +} + +//Fixes a bug with VS2019 (https://developercommunity.visualstudio.com/content/problem/1244809/error-cs0518-predefined-type-systemruntimecompiler.html) +namespace System.Runtime.CompilerServices { internal static class IsExternalInit { } } \ No newline at end of file diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs index a2c97c8d..5db02dc8 100644 --- a/XmlSchemaClassGenerator/ModelBuilder.cs +++ b/XmlSchemaClassGenerator/ModelBuilder.cs @@ -29,7 +29,7 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) _configuration = configuration; _set = set; - DocumentationModel.DisableComments = _configuration.DisableComments; + GeneratorModel.DisableComments = _configuration.DisableComments; var objectModel = new SimpleModel(_configuration) { Name = "AnyType", diff --git a/XmlSchemaClassGenerator/RestrictionModel.cs b/XmlSchemaClassGenerator/RestrictionModel.cs index 79c57be5..1976ef85 100644 --- a/XmlSchemaClassGenerator/RestrictionModel.cs +++ b/XmlSchemaClassGenerator/RestrictionModel.cs @@ -1,28 +1,13 @@ using System; using System.CodeDom; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace XmlSchemaClassGenerator { - public abstract class RestrictionModel + public abstract class RestrictionModel : GeneratorModel { - public GeneratorConfiguration Configuration { get; private set; } + protected RestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - protected RestrictionModel(GeneratorConfiguration configuration) - { - Configuration = configuration; - } - - public bool IsSupported - { - get - { - return MinimumDataAnnotationMode >= Configuration.DataAnnotationMode; - } - } + public bool IsSupported => MinimumDataAnnotationMode >= Configuration.DataAnnotationMode; /// /// The DataAnnotationMode required to be able to emit this restriction @@ -34,281 +19,126 @@ public bool IsSupported public abstract class ValueRestrictionModel : RestrictionModel { - protected ValueRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { - - } + protected ValueRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } public T Value { get; set; } - public override CodeAttributeDeclaration GetAttribute() - { - return null; - } + public override CodeAttributeDeclaration GetAttribute() => null; } - public abstract class ValueTypeRestrictionModel: ValueRestrictionModel + public abstract class ValueTypeRestrictionModel : ValueRestrictionModel { - protected ValueTypeRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { - - } + protected ValueTypeRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } public Type Type { get; set; } } public class MinMaxLengthRestrictionModel : RestrictionModel { - public MinMaxLengthRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { - - } + public MinMaxLengthRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } public int Min { get; set; } public int Max { get; set; } - public override string Description - { - get - { - var s = ""; - if (Min > 0) { s += string.Format("Minimum length: {0}. ", Min); } - if (Max > 0) { s += string.Format("Maximum length: {0}.", Max); } - return s.Trim(); - } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.Partial; - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.Partial; } - } + public override string Description => ((Min > 0 ? $"Minimum length: {Min}. " : string.Empty) + (Max > 0 ? $"Maximum length: {Max}." : string.Empty)).Trim(); public override CodeAttributeDeclaration GetAttribute() { - var a = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "StringLengthAttribute", Configuration), - new CodeAttributeArgument(Max > 0 ? (CodeExpression)new CodePrimitiveExpression(Max) : new CodeSnippetExpression("int.MaxValue"))); - if (Min > 0) { a.Arguments.Add(new CodeAttributeArgument("MinimumLength", new CodePrimitiveExpression(Min))); } - + var a = AttributeDecl(Attributes.StringLength, new(Max > 0 ? new CodePrimitiveExpression(Max) : new CodeSnippetExpression("int.MaxValue"))); + if (Min > 0) { a.Arguments.Add(new("MinimumLength", new CodePrimitiveExpression(Min))); } return a; } } public class MaxLengthRestrictionModel : ValueRestrictionModel { - public MaxLengthRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { + public MaxLengthRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - } - - public override string Description - { - get - { - return string.Format("Maximum length: {0}.", Value); - } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } + public override string Description => string.Format("Maximum length: {0}.", Value); public override CodeAttributeDeclaration GetAttribute() - { - return new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "MaxLengthAttribute", Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(Value))); - } + => AttributeDecl(Attributes.MaxLength, new(new CodePrimitiveExpression(Value))); } public class MinLengthRestrictionModel : ValueRestrictionModel { - public MinLengthRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { + public MinLengthRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override string Description - { - get - { - return string.Format("Minimum length: {0}.", Value); - } - } - - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } + public override string Description => string.Format("Minimum length: {0}.", Value); public override CodeAttributeDeclaration GetAttribute() - { - return new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "MinLengthAttribute", Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(Value))); - } + => AttributeDecl(Attributes.MinLength, new(new CodePrimitiveExpression(Value))); } public class TotalDigitsRestrictionModel : ValueRestrictionModel { - public TotalDigitsRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { + public TotalDigitsRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } - - public override string Description - { - get - { - return string.Format("Total number of digits: {0}.", Value); - } - } + public override string Description => string.Format("Total number of digits: {0}.", Value); } public class FractionDigitsRestrictionModel : ValueRestrictionModel { - public FractionDigitsRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { - - } + public FractionDigitsRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override string Description - { - get - { - return string.Format("Total number of digits in fraction: {0}.", Value); - } - } + public override string Description => $"Total number of digits in fraction: {Value}."; } public class PatternRestrictionModel : ValueRestrictionModel { - public PatternRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { + public PatternRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.Partial; - public override string Description - { - get - { - return string.Format("Pattern: {0}.", Value); - } - } - - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.Partial; } - } + public override string Description => $"Pattern: {Value}."; public override CodeAttributeDeclaration GetAttribute() - { - return new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "RegularExpressionAttribute", Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(Value))); - } + => AttributeDecl(Attributes.RegularExpression, new(new CodePrimitiveExpression(Value))); } - public class MinInclusiveRestrictionModel: ValueTypeRestrictionModel + public class MinInclusiveRestrictionModel : ValueTypeRestrictionModel { - public MinInclusiveRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { + public MinInclusiveRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } - - public override string Description - { - get - { - return string.Format("Minimum inclusive value: {0}.", Value); - } - } + public override string Description => $"Minimum inclusive value: {Value}."; } - public class MinExclusiveRestrictionModel: ValueTypeRestrictionModel + public class MinExclusiveRestrictionModel : ValueTypeRestrictionModel { - public MinExclusiveRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { - - } + public MinExclusiveRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override string Description - { - get - { - return string.Format("Minimum exclusive value: {0}.", Value); - } - } + public override string Description => $"Minimum exclusive value: {Value}."; } - public class MaxInclusiveRestrictionModel: ValueTypeRestrictionModel + public class MaxInclusiveRestrictionModel : ValueTypeRestrictionModel { - public MaxInclusiveRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { - - } + public MaxInclusiveRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override string Description - { - get - { - return string.Format("Maximum inclusive value: {0}.", Value); - } - } + public override string Description => $"Maximum inclusive value: {Value}."; } - public class MaxExclusiveRestrictionModel: ValueTypeRestrictionModel + public class MaxExclusiveRestrictionModel : ValueTypeRestrictionModel { - public MaxExclusiveRestrictionModel(GeneratorConfiguration configuration) - : base(configuration) - { + public MaxExclusiveRestrictionModel(GeneratorConfiguration configuration) : base(configuration) { } - } + public override DataAnnotationMode MinimumDataAnnotationMode => DataAnnotationMode.All; - public override DataAnnotationMode MinimumDataAnnotationMode - { - get { return DataAnnotationMode.All; } - } - - public override string Description - { - get - { - return string.Format("Maximum exclusive value: {0}.", Value); - } - } + public override string Description => $"Maximum exclusive value: {Value}."; } } diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs index afb8714b..2f1f78c0 100644 --- a/XmlSchemaClassGenerator/TypeModel.cs +++ b/XmlSchemaClassGenerator/TypeModel.cs @@ -13,20 +13,18 @@ namespace XmlSchemaClassGenerator { - public class NamespaceModel + public class NamespaceModel : GeneratorModel { public string Name { get; set; } - public NamespaceKey Key { get; private set; } + public NamespaceKey Key { get; } public Dictionary Types { get; set; } /// /// Does the namespace of this type clashes with a class in the same or upper namespace? /// public bool IsAmbiguous { get; set; } - public GeneratorConfiguration Configuration { get; private set; } - public NamespaceModel(NamespaceKey key, GeneratorConfiguration configuration) + public NamespaceModel(NamespaceKey key, GeneratorConfiguration configuration) : base(configuration) { - Configuration = configuration; Key = key; Types = new Dictionary(); } @@ -38,9 +36,7 @@ public static CodeNamespace Generate(string namespaceName, IEnumerable n.Condition(conf)).OrderBy(n => n.Namespace)) codeNamespace.Imports.Add(new CodeNamespaceImport(Namespace)); - var typeModels = parts.SelectMany(x => x.Types.Values).ToList(); - - foreach (var typeModel in typeModels) + foreach (var typeModel in parts.SelectMany(x => x.Types.Values).ToList()) { var type = typeModel.Generate(); if (type != null) @@ -57,53 +53,10 @@ public class DocumentationModel { public string Language { get; set; } public string Text { get; set; } - public static bool DisableComments { get; set; } - - public static IEnumerable GetComments(IList docs, GeneratorConfiguration conf) - { - if (DisableComments || docs.Count == 0) - yield break; - - yield return new CodeCommentStatement("", true); - - foreach (var doc in docs - .Where(d => string.IsNullOrEmpty(d.Language) || conf.CommentLanguages.Any(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase))) - .OrderBy(d => d.Language)) - { - var text = doc.Text; - var comment = string.Format(@"{1}", - string.IsNullOrEmpty(doc.Language) ? "" : string.Format(@" xml:lang=""{0}""", doc.Language), CodeUtilities.NormalizeNewlines(text).Trim()); - yield return new CodeCommentStatement(comment, true); - } - - yield return new CodeCommentStatement("", true); - } - - public static void AddDescription(CodeAttributeDeclarationCollection attributes, IEnumerable docs, GeneratorConfiguration conf) - { - if (!conf.GenerateDescriptionAttribute || DisableComments || !docs.Any()) return; - - var doc = GetSingleDoc(docs.Where(d => string.IsNullOrEmpty(d.Language) || conf.CommentLanguages.Any(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase)))); - - if (doc != null) - { - var descriptionAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(DescriptionAttribute), conf), - new CodeAttributeArgument(new CodePrimitiveExpression(Regex.Replace(doc.Text, @"\s+", " ").Trim()))); - attributes.Add(descriptionAttribute); - } - } - - private static DocumentationModel GetSingleDoc(IEnumerable docs) - { - if (docs.Count() == 1) return docs.Single(); - var englishDoc = docs.FirstOrDefault(d => string.IsNullOrEmpty(d.Language) || d.Language.StartsWith("en", StringComparison.OrdinalIgnoreCase)); - if (englishDoc != null) return englishDoc; - return docs.FirstOrDefault(); - } } [DebuggerDisplay("{Name}")] - public abstract class TypeModel + public abstract class TypeModel : GeneratorModel { protected static readonly CodeDomProvider CSharpProvider = CodeDomProvider.CreateProvider("CSharp"); @@ -114,28 +67,23 @@ public abstract class TypeModel public string Name { get; set; } public XmlQualifiedName XmlSchemaName { get; set; } public XmlSchemaType XmlSchemaType { get; set; } - public List Documentation { get; private set; } + public List Documentation { get; } = new(); public bool IsAnonymous { get; set; } - public GeneratorConfiguration Configuration { get; private set; } public virtual bool IsSubtype => false; - protected TypeModel(GeneratorConfiguration configuration) - { - Configuration = configuration; - Documentation = new List(); - } + protected TypeModel(GeneratorConfiguration configuration) : base(configuration) { } public virtual CodeTypeDeclaration Generate() { var typeDeclaration = new CodeTypeDeclaration { Name = Name }; - typeDeclaration.Comments.AddRange(DocumentationModel.GetComments(Documentation, Configuration).ToArray()); + typeDeclaration.Comments.AddRange(GetComments(Documentation).ToArray()); - DocumentationModel.AddDescription(typeDeclaration.CustomAttributes, Documentation, Configuration); + AddDescription(typeDeclaration.CustomAttributes, Documentation); - var generatedAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(GeneratedCodeAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(Configuration.Version.Title)), - new CodeAttributeArgument(new CodePrimitiveExpression(Configuration.CreateGeneratedCodeAttributeVersion ? Configuration.Version.Version : ""))); + var generatedAttribute = AttributeDecl( + new(new CodePrimitiveExpression(Configuration.Version.Title)), + new(new CodePrimitiveExpression(Configuration.CreateGeneratedCodeAttributeVersion ? Configuration.Version.Version : ""))); typeDeclaration.CustomAttributes.Add(generatedAttribute); return typeDeclaration; @@ -143,29 +91,24 @@ public virtual CodeTypeDeclaration Generate() protected void GenerateTypeAttribute(CodeTypeDeclaration typeDeclaration) { - if (XmlSchemaName != null) - { - var typeAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlTypeAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name)), - new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(XmlSchemaName.Namespace))); - if (IsAnonymous && !IsSubtype) - { - // don't generate AnonymousType if it's derived class, otherwise XmlSerializer will - // complain with "InvalidOperationException: Cannot include anonymous type '...'" - typeAttribute.Arguments.Add(new CodeAttributeArgument("AnonymousType", new CodePrimitiveExpression(true))); - } - typeDeclaration.CustomAttributes.Add(typeAttribute); - } + if (XmlSchemaName == null) return; + + var typeAttribute = AttributeDecl( + new(new CodePrimitiveExpression(XmlSchemaName.Name)), + new(nameof(XmlRootAttribute.Namespace), new CodePrimitiveExpression(XmlSchemaName.Namespace))); + + // don't generate AnonymousType if it's derived class, otherwise XmlSerializer will + // complain with "InvalidOperationException: Cannot include anonymous type '...'" + if (IsAnonymous && !IsSubtype) + typeAttribute.Arguments.Add(new("AnonymousType", new CodePrimitiveExpression(true))); + + typeDeclaration.CustomAttributes.Add(typeAttribute); } protected void GenerateSerializableAttribute(CodeTypeDeclaration typeDeclaration) { if (Configuration.GenerateSerializableAttribute) - { - var serializableAttribute = - new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(SerializableAttribute), Configuration)); - typeDeclaration.CustomAttributes.Add(serializableAttribute); - } + typeDeclaration.CustomAttributes.Add(AttributeDecl()); } public virtual CodeTypeReference GetReferenceFor(NamespaceModel referencingNamespace, bool collection = false, bool forInit = false, bool attribute = false) @@ -179,21 +122,20 @@ public virtual CodeTypeReference GetReferenceFor(NamespaceModel referencingNames } else if ((referencingNamespace ?? Namespace).IsAmbiguous) { - name = string.Format("global::{0}.{1}", Namespace.Name, Name); + name = $"global::{Namespace.Name}.{Name}"; referencingOptions = CodeTypeReferenceOptions.GenericTypeParameter; } else { - name = string.Format("{0}.{1}", Namespace.Name, Name); + name = $"{Namespace.Name}.{Name}"; } if (collection) { name = forInit ? SimpleModel.GetCollectionImplementationName(name, Configuration) : SimpleModel.GetCollectionDefinitionName(name, Configuration); - if (Configuration.CollectionType == typeof(System.Array)) - referencingOptions = CodeTypeReferenceOptions.GenericTypeParameter; - else - referencingOptions = Configuration.CodeTypeReferenceOptions; + referencingOptions = Configuration.CollectionType == typeof(Array) + ? CodeTypeReferenceOptions.GenericTypeParameter + : Configuration.CodeTypeReferenceOptions; } return new CodeTypeReference(name, referencingOptions); @@ -227,7 +169,6 @@ public override CodeTypeDeclaration Generate() interfaceDeclaration.TypeAttributes = (interfaceDeclaration.TypeAttributes & ~System.Reflection.TypeAttributes.VisibilityMask) | System.Reflection.TypeAttributes.NestedAssembly; } - foreach (var property in Properties) property.AddInterfaceMembersTo(interfaceDeclaration); @@ -250,23 +191,23 @@ public IEnumerable AllDerivedReferenceTypes(List(), Attributes = MemberAttributes.Public, }; classDeclaration.Members.Add(propertyChangedEvent); @@ -347,16 +288,16 @@ public override CodeTypeDeclaration Generate() Configuration.MemberVisitor(propertyChangedEvent, propertyChangedModel); + var param = new CodeParameterDeclarationExpression(typeof(string), "propertyName"); + var threadSafeDelegateInvokeExpression = new CodeSnippetExpression($"{propertyChangedEvent.Name}?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs({param.Name}))"); var onPropChangedMethod = new CodeMemberMethod { - Name = "OnPropertyChanged", + Name = OnPropertyChanged, Attributes = MemberAttributes.Family, + Parameters = { param }, + Statements = { threadSafeDelegateInvokeExpression } }; - var param = new CodeParameterDeclarationExpression(typeof(string), "propertyName"); - onPropChangedMethod.Parameters.Add(param); - var threadSafeDelegateInvokeExpression = new CodeSnippetExpression($"{propertyChangedEvent.Name}?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs({param.Name}))"); - onPropChangedMethod.Statements.Add(threadSafeDelegateInvokeExpression); classDeclaration.Members.Add(onPropChangedMethod); } @@ -387,31 +328,26 @@ public override CodeTypeDeclaration Generate() else { // hack to generate automatic property - member.Name += " { get; set; }"; + member.Name += GetSet; } - var docs = new List { new DocumentationModel { Language = "en", Text = "Gets or sets the text value." }, - new DocumentationModel { Language = "de", Text = "Ruft den Text ab oder legt diesen fest." } }; + var docs = new List { + new() { Language = English, Text = "Gets or sets the text value." }, + new() { Language = German, Text = "Ruft den Text ab oder legt diesen fest." } + }; - var attribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlTextAttribute), Configuration)); + var attribute = AttributeDecl(); if (BaseClass is SimpleModel simpleModel) { - docs.AddRange(simpleModel.Restrictions.Select(r => new DocumentationModel { Language = "en", Text = r.Description })); + docs.AddRange(simpleModel.Restrictions.Select(r => new DocumentationModel { Language = English, Text = r.Description })); member.CustomAttributes.AddRange(simpleModel.GetRestrictionAttributes().ToArray()); - if (simpleModel.XmlSchemaType.Datatype.IsDataTypeAttributeAllowed() ?? simpleModel.UseDataTypeAttribute) - { - var name = BaseClass.GetQualifiedName(); - if (name.Namespace == XmlSchema.Namespace) - { - var dataType = new CodeAttributeArgument("DataType", new CodePrimitiveExpression(name.Name)); - attribute.Arguments.Add(dataType); - } - } + if (BaseClass.GetQualifiedName() is { Namespace: XmlSchema.Namespace, Name: var name } && (simpleModel.XmlSchemaType.Datatype.IsDataTypeAttributeAllowed() ?? simpleModel.UseDataTypeAttribute)) + attribute.Arguments.Add(new CodeAttributeArgument(nameof(XmlTextAttribute.DataType), new CodePrimitiveExpression(name))); } - member.Comments.AddRange(DocumentationModel.GetComments(docs, Configuration).ToArray()); + member.Comments.AddRange(GetComments(docs).ToArray()); member.CustomAttributes.Add(attribute); classDeclaration.Members.Add(member); @@ -429,14 +365,14 @@ public override CodeTypeDeclaration Generate() if (Configuration.EnableDataBinding) { - classDeclaration.BaseTypes.Add(CodeUtilities.CreateTypeReference(typeof(INotifyPropertyChanged), Configuration)); + classDeclaration.BaseTypes.Add(TypeRef()); } if (Configuration.EntityFramework && BaseClass is not ClassModel) { // generate key - var keyProperty = Properties.FirstOrDefault(p => p.Name.ToLowerInvariant() == "id") - ?? Properties.FirstOrDefault(p => p.Name.ToLowerInvariant() == (Name.ToLowerInvariant() + "id")); + var keyProperty = Properties.Find(p => string.Equals(p.Name, "id", StringComparison.InvariantCultureIgnoreCase)) + ?? Properties.Find(p => p.Name.ToLowerInvariant() == Name.ToLowerInvariant() + "id"); if (keyProperty == null) { @@ -445,8 +381,10 @@ public override CodeTypeDeclaration Generate() Name = "Id", Type = new SimpleModel(Configuration) { ValueType = typeof(long) }, OwningType = this, - Documentation = { new DocumentationModel { Language = "en", Text = "Gets or sets a value uniquely identifying this entity." }, - new DocumentationModel { Language = "de", Text = "Ruft einen Wert ab, der diese Entität eindeutig identifiziert, oder legt diesen fest." } } + Documentation = { + new() { Language = English, Text = "Gets or sets a value uniquely identifying this entity." }, + new() { Language = German, Text = "Ruft einen Wert ab, der diese Entität eindeutig identifiziert, oder legt diesen fest." } + } }; Properties.Insert(0, keyProperty); } @@ -454,20 +392,18 @@ public override CodeTypeDeclaration Generate() keyProperty.IsKey = true; } - foreach (var property in Properties.GroupBy(x => x.Name).Select(g => g.Select((p, i) => (Property: p, Index: i)).ToList())) + var properties = Properties.GroupBy(x => x.Name).SelectMany(g => g.Select((p, i) => (Property: p, Index: i)).ToList()); + foreach (var (Property, Index) in properties) { - foreach (var p in property) + if (Index > 0) { - if (p.Index > 0) - { - p.Property.Name += $"_{p.Index + 1}"; - - if (property.Any(q => p.Property.XmlSchemaName == q.Property.XmlSchemaName && q.Index < p.Index)) - continue; - } + Property.Name += $"_{Index + 1}"; - p.Property.AddMembersTo(classDeclaration, Configuration.EnableDataBinding); + if (properties.Any(q => Property.XmlSchemaName == q.Property.XmlSchemaName && q.Index < Index)) + continue; } + + Property.AddMembersTo(classDeclaration, Configuration.EnableDataBinding); } if (IsMixed && (BaseClass == null || (BaseClass is ClassModel && !AllBaseClasses.Any(b => b.IsMixed)))) @@ -481,9 +417,9 @@ public override CodeTypeDeclaration Generate() } var text = new CodeMemberField(typeof(string[]), propName); // hack to generate automatic property - text.Name += " { get; set; }"; + text.Name += GetSet; text.Attributes = MemberAttributes.Public; - var xmlTextAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlTextAttribute), Configuration)); + var xmlTextAttribute = AttributeDecl(); text.CustomAttributes.Add(xmlTextAttribute); classDeclaration.Members.Add(text); @@ -497,32 +433,25 @@ public override CodeTypeDeclaration Generate() Configuration.MemberVisitor(text, textPropertyModel); } + var customAttributes = classDeclaration.CustomAttributes; + if (Configuration.GenerateDebuggerStepThroughAttribute) - classDeclaration.CustomAttributes.Add( - new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(DebuggerStepThroughAttribute), Configuration))); + customAttributes.Add(AttributeDecl()); if (Configuration.GenerateDesignerCategoryAttribute) - { - classDeclaration.CustomAttributes.Add( - new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(DesignerCategoryAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression("code")))); - } + customAttributes.Add(AttributeDecl(new CodeAttributeArgument(new CodePrimitiveExpression("code")))); if (RootElementName != null) { - var rootAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlRootAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(RootElementName.Name)), - new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(RootElementName.Namespace))); - classDeclaration.CustomAttributes.Add(rootAttribute); + var rootAttribute = AttributeDecl( + new(new CodePrimitiveExpression(RootElementName.Name)), + new(nameof(XmlRootAttribute.Namespace), new CodePrimitiveExpression(RootElementName.Namespace))); + customAttributes.Add(rootAttribute); } var derivedTypes = GetAllDerivedTypes(); foreach (var derivedType in derivedTypes.OrderBy(t => t.Name)) - { - var includeAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlIncludeAttribute), Configuration), - new CodeAttributeArgument(new CodeTypeOfExpression(derivedType.GetReferenceFor(Namespace)))); - classDeclaration.CustomAttributes.Add(includeAttribute); - } + customAttributes.Add(AttributeDecl(new CodeAttributeArgument(new CodeTypeOfExpression(derivedType.GetReferenceFor(Namespace))))); classDeclaration.BaseTypes.AddRange(Interfaces.Select(i => i.GetReferenceFor(Namespace)).ToArray()); @@ -560,8 +489,7 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att reference = writer.ToString(); } - var dv = new CodeSnippetExpression($"new { reference } {{ { Configuration.TextValuePropertyName } = { val } }};"); - return dv; + return new CodeSnippetExpression($"new {reference} {{ {Configuration.TextValuePropertyName} = {val} }};"); } return base.GetDefaultValueFor(defaultString, attribute); @@ -594,8 +522,12 @@ public void AddInterfaces(IEnumerable interfaces) } [DebuggerDisplay("{Name}")] - public class PropertyModel + public class PropertyModel : GeneratorModel { + private const string Value = nameof(Value); + private const string Specified = nameof(Specified); + private const string Namespace = nameof(XmlRootAttribute.Namespace); + public TypeModel OwningType { get; set; } public string Name { get; set; } public string OriginalPropertyName { get; set; } @@ -608,7 +540,7 @@ public class PropertyModel public string FixedValue { get; set; } public XmlSchemaForm Form { get; set; } public string XmlNamespace { get; set; } - public List Documentation { get; private set; } + public List Documentation { get; } public bool IsDeprecated { get; set; } public XmlQualifiedName XmlSchemaName { get; set; } public bool IsAny { get; set; } @@ -617,215 +549,103 @@ public class PropertyModel public XmlSchemaParticle XmlParticle { get; set; } public XmlSchemaObject XmlParent { get; set; } public Particle Particle { get; set; } - public GeneratorConfiguration Configuration { get; private set; } public List Substitutes { get; set; } - public PropertyModel(GeneratorConfiguration configuration) + public PropertyModel(GeneratorConfiguration configuration) : base(configuration) { - Configuration = configuration; Documentation = new List(); Substitutes = new List(); } internal static string GetAccessors(string memberName, string backingFieldName, PropertyValueTypeCode typeCode, bool privateSetter, bool withDataBinding = true) { - if (withDataBinding) - { - switch (typeCode) - { - case PropertyValueTypeCode.ValueType: - return CodeUtilities.NormalizeNewlines(string.Format(@" - {{ - get - {{ - return {0}; - }} - {2}set - {{ - if (!{0}.Equals(value)) - {{ - {0} = value; - OnPropertyChanged(nameof({1})); - }} - }} - }}", backingFieldName, memberName, (privateSetter ? "private " : string.Empty))); - case PropertyValueTypeCode.Other: - return CodeUtilities.NormalizeNewlines(string.Format(@" - {{ - get - {{ - return {0}; - }} - {2}set - {{ - if ({0} == value) - return; - if ({0} == null || value == null || !{0}.Equals(value)) - {{ - {0} = value; - OnPropertyChanged(nameof({1})); - }} - }} - }}", backingFieldName, memberName, (privateSetter ? "private " : string.Empty))); - case PropertyValueTypeCode.Array: - return CodeUtilities.NormalizeNewlines(string.Format(@" - {{ - get - {{ - return {0}; - }} - {2}set - {{ - if ({0} == value) - return; - if ({0} == null || value == null || !{0}.SequenceEqual(value)) - {{ - {0} = value; - OnPropertyChanged(nameof({1})); - }} - }} - }}", backingFieldName, memberName, (privateSetter ? "private " : string.Empty))); - } - } + string assign = $@" + {backingFieldName} = value;"; - if (privateSetter) - { - return CodeUtilities.NormalizeNewlines(string.Format(@" + return CodeUtilities.NormalizeNewlines($@" {{ get {{ - return this.{0}; + return {backingFieldName}; }} - private set - {{ - this.{0} = value; + {(privateSetter ? "private " : string.Empty)}set + {{{(typeCode, withDataBinding) switch + { + (PropertyValueTypeCode.ValueType, true) => $@" + if (!{backingFieldName}.Equals(value)) + {{{assign} + OnPropertyChanged(nameof({memberName})); + }}", + (PropertyValueTypeCode.Other or PropertyValueTypeCode.Array, true) => $@" + if ({backingFieldName} == value) + return; + if ({backingFieldName} == null || value == null || !{backingFieldName}.{(typeCode is PropertyValueTypeCode.Other ? EqualsMethod : nameof(Enumerable.SequenceEqual))}(value)) + {{{assign} + OnPropertyChanged(nameof({memberName})); + }}", + _ => assign, }} - }}", backingFieldName)); - } - else - { - return CodeUtilities.NormalizeNewlines(string.Format(@" - {{ - get - {{ - return this.{0}; }} - set - {{ - this.{0} = value; - }} - }}", backingFieldName)); - } + }}"); } - private ClassModel TypeClassModel - { - get { return Type as ClassModel; } - } + private ClassModel TypeClassModel => Type as ClassModel; /// /// A property is an array if it is a sequence containing a single element with maxOccurs > 1. /// - public bool IsArray - { - get - { - return Configuration.UseArrayItemAttribute + public bool IsArray => Configuration.UseArrayItemAttribute && !IsCollection && !IsAttribute && !IsList && TypeClassModel != null && TypeClassModel.BaseClass == null && TypeClassModel.Properties.Count == 1 && !TypeClassModel.Properties[0].IsAttribute && !TypeClassModel.Properties[0].IsAny && TypeClassModel.Properties[0].IsCollection; - } - } - private TypeModel PropertyType - { - get { return !IsArray ? Type : TypeClassModel.Properties[0].Type; } - } + private TypeModel PropertyType => !IsArray ? Type : TypeClassModel.Properties[0].Type; - private bool IsNullableValueType - { - get - { - return DefaultValue == null + private bool IsNullableValueType => DefaultValue == null && IsNullable && !(IsCollection || IsArray) && !IsList && ((PropertyType is EnumModel) || (PropertyType is SimpleModel model && model.ValueType.IsValueType)); - } - } - private bool IsNullableReferenceType - { - get - { - return DefaultValue == null - && IsNullable && (IsCollection || IsArray || IsList || PropertyType is ClassModel || PropertyType is SimpleModel model && !model.ValueType.IsValueType); - } - } + private bool IsNullableReferenceType => DefaultValue == null + && IsNullable && (IsCollection || IsArray || IsList || PropertyType is ClassModel || (PropertyType is SimpleModel model && !model.ValueType.IsValueType)); - private bool IsNillableValueType - { - get - { - return IsNillable + private bool IsNillableValueType => IsNillable && !(IsCollection || IsArray) && ((PropertyType is EnumModel) || (PropertyType is SimpleModel model && model.ValueType.IsValueType)); - } - } - private bool IsList - { - get - { - return Type.XmlSchemaType?.Datatype?.Variety == XmlSchemaDatatypeVariety.List; - } - } + private bool IsList => Type.XmlSchemaType?.Datatype?.Variety == XmlSchemaDatatypeVariety.List; - private bool IsPrivateSetter - { - get - { - return Configuration.CollectionSettersMode == CollectionSettersMode.Private + private bool IsPrivateSetter => Configuration.CollectionSettersMode == CollectionSettersMode.Private && (IsCollection || IsArray || (IsList && IsAttribute)); - } - } - private CodeTypeReference TypeReference - { - get - { - return PropertyType.GetReferenceFor(OwningType.Namespace, + private CodeTypeReference TypeReference => PropertyType.GetReferenceFor(OwningType.Namespace, collection: IsCollection || IsArray || (IsList && IsAttribute), attribute: IsAttribute); - } - } private void AddDocs(CodeTypeMember member) { var docs = new List(Documentation); - DocumentationModel.AddDescription(member.CustomAttributes, docs, Configuration); + AddDescription(member.CustomAttributes, docs); if (PropertyType is SimpleModel simpleType) { docs.AddRange(simpleType.Documentation); - docs.AddRange(simpleType.Restrictions.Select(r => new DocumentationModel { Language = "en", Text = r.Description })); + docs.AddRange(simpleType.Restrictions.Select(r => new DocumentationModel { Language = English, Text = r.Description })); member.CustomAttributes.AddRange(simpleType.GetRestrictionAttributes().ToArray()); } - member.Comments.AddRange(DocumentationModel.GetComments(docs, Configuration).ToArray()); + member.Comments.AddRange(GetComments(docs).ToArray()); } private CodeAttributeDeclaration CreateDefaultValueAttribute(CodeTypeReference typeReference, CodeExpression defaultValueExpression) { - var defaultValueAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(DefaultValueAttribute), Configuration)); - if (typeReference.BaseType == "System.Decimal") - { - defaultValueAttribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(typeof(decimal)))); - defaultValueAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(DefaultValue))); - } - else - defaultValueAttribute.Arguments.Add(new CodeAttributeArgument(defaultValueExpression)); + var defaultValueAttribute = AttributeDecl(); + + defaultValueAttribute.Arguments.AddRange(typeReference.BaseType == typeof(decimal).FullName + ? new CodeAttributeArgument[] { new(new CodeTypeOfExpression(typeof(decimal))), new(new CodePrimitiveExpression(DefaultValue)) } + : new CodeAttributeArgument[] { new(defaultValueExpression) }); return defaultValueAttribute; } @@ -840,11 +660,7 @@ public void AddInterfaceMembersTo(CodeTypeDeclaration typeDeclaration) var typeReference = TypeReference; if (isNullableValueType && Configuration.GenerateNullables) - { - var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); - nullableType.TypeArguments.Add(typeReference); - typeReference = nullableType; - } + typeReference = NullableTypeRef(typeReference); member = new CodeMemberProperty { @@ -858,8 +674,7 @@ public void AddInterfaceMembersTo(CodeTypeDeclaration typeDeclaration) { var defaultValueExpression = propertyType.GetDefaultValueFor(DefaultValue, IsAttribute); - if ((defaultValueExpression is CodePrimitiveExpression) || (defaultValueExpression is CodeFieldReferenceExpression) - && !CodeUtilities.IsXmlLangOrSpace(XmlSchemaName)) + if ((defaultValueExpression is CodePrimitiveExpression or CodeFieldReferenceExpression) && !CodeUtilities.IsXmlLangOrSpace(XmlSchemaName)) { var defaultValueAttribute = CreateDefaultValueAttribute(typeReference, defaultValueExpression); member.CustomAttributes.Add(defaultValueAttribute); @@ -874,7 +689,8 @@ public void AddInterfaceMembersTo(CodeTypeDeclaration typeDeclaration) // ReSharper disable once FunctionComplexityOverflow public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBinding) { - CodeTypeMember member; + // Note: We use CodeMemberField because CodeMemberProperty doesn't allow for private set + var member = new CodeMemberField() { Name = Name }; var typeClassModel = TypeClassModel; var isArray = IsArray; @@ -884,101 +700,60 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi var isPrivateSetter = IsPrivateSetter; var typeReference = TypeReference; - var requiresBackingField = withDataBinding || DefaultValue != null || IsCollection || isArray; - CodeMemberField backingField; + CodeAttributeDeclaration ignoreAttribute = new(TypeRef()); + CodeAttributeDeclaration notMappedAttribute = new(CodeUtilities.CreateTypeReference(Attributes.NotMapped, Configuration)); - if (IsNillableValueType) - { - var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); - nullableType.TypeArguments.Add(typeReference); - backingField = new CodeMemberField(nullableType, OwningType.GetUniqueFieldName(this)); - } - else - { - backingField = new CodeMemberField(typeReference, OwningType.GetUniqueFieldName(this)) - { - Attributes = MemberAttributes.Private - }; - } - - var ignoreAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlIgnoreAttribute), Configuration)); - var notMappedAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations.Schema", "NotMappedAttribute", Configuration)); - backingField.CustomAttributes.Add(ignoreAttribute); - - if (requiresBackingField) + CodeMemberField backingField = null; + if (withDataBinding || DefaultValue != null || IsCollection || isArray) { + backingField = IsNillableValueType + ? new CodeMemberField(NullableTypeRef(typeReference), OwningType.GetUniqueFieldName(this)) + : new CodeMemberField(typeReference, OwningType.GetUniqueFieldName(this)) { Attributes = MemberAttributes.Private }; + backingField.CustomAttributes.Add(ignoreAttribute); typeDeclaration.Members.Add(backingField); } if (DefaultValue == null || ((IsCollection || isArray || (IsList && IsAttribute)) && IsNullable)) { - var propertyName = Name; - if (isNullableValueType && Configuration.GenerateNullables && !(Configuration.UseShouldSerializePattern && !IsAttribute)) - { - propertyName += "Value"; - } + member.Name += Value; if (IsNillableValueType) { - var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); - nullableType.TypeArguments.Add(typeReference); - member = new CodeMemberField(nullableType, propertyName); + member.Type = NullableTypeRef(typeReference); } else if (isNullableValueType && !IsAttribute && Configuration.UseShouldSerializePattern) { - var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); - nullableType.TypeArguments.Add(typeReference); - member = new CodeMemberField(nullableType, propertyName); + member.Type = NullableTypeRef(typeReference); typeDeclaration.Members.Add(new CodeMemberMethod { Attributes = MemberAttributes.Public, - Name = "ShouldSerialize" + propertyName, + Name = "ShouldSerialize" + member.Name, ReturnType = new CodeTypeReference(typeof(bool)), - Statements = - { - new CodeSnippetExpression($"return {propertyName}.HasValue") - } + Statements = { new CodeSnippetExpression($"return {member.Name}.{HasValue}") } }); } else - member = new CodeMemberField(typeReference, propertyName); - - if (requiresBackingField) { - member.Name += GetAccessors(member.Name, backingField.Name, - IsCollection || isArray ? PropertyValueTypeCode.Array : propertyType.GetPropertyValueTypeCode(), - isPrivateSetter, withDataBinding); - } - else - { - // hack to generate automatic property - member.Name += isPrivateSetter ? " { get; private set; }" : " { get; set; }"; + member.Type = typeReference; } + + member.Name += backingField != null + ? GetAccessors(member.Name, backingField.Name, IsCollection || isArray ? PropertyValueTypeCode.Array : propertyType.GetPropertyValueTypeCode(), isPrivateSetter, withDataBinding) + : $" {{ get; {(isPrivateSetter ? "private " : string.Empty)}set; }}"; // hack to generate automatic property } else { var defaultValueExpression = propertyType.GetDefaultValueFor(DefaultValue, IsAttribute); backingField.InitExpression = defaultValueExpression; - if (IsNillableValueType) - { - var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); - nullableType.TypeArguments.Add(typeReference); - member = new CodeMemberField(nullableType, Name); - } - else - member = new CodeMemberField(typeReference, Name); + member.Type = IsNillableValueType ? NullableTypeRef(typeReference) : typeReference; member.Name += GetAccessors(member.Name, backingField.Name, propertyType.GetPropertyValueTypeCode(), false, withDataBinding); - if (IsNullable && ((defaultValueExpression is CodePrimitiveExpression) || (defaultValueExpression is CodeFieldReferenceExpression)) - && !CodeUtilities.IsXmlLangOrSpace(XmlSchemaName)) - { - var defaultValueAttribute = CreateDefaultValueAttribute(typeReference, defaultValueExpression); - member.CustomAttributes.Add(defaultValueAttribute); - } + if (IsNullable && (defaultValueExpression is CodePrimitiveExpression or CodeFieldReferenceExpression) && !CodeUtilities.IsXmlLangOrSpace(XmlSchemaName)) + member.CustomAttributes.Add(CreateDefaultValueAttribute(typeReference, defaultValueExpression)); } member.Attributes = MemberAttributes.Public; @@ -988,7 +763,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi if (!IsNullable && Configuration.DataAnnotationMode != DataAnnotationMode.None) { - var requiredAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "RequiredAttribute", Configuration)); + var requiredAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(Attributes.Required, Configuration)); member.CustomAttributes.Add(requiredAttribute); } @@ -1008,34 +783,31 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi generateSpecifiedProperty = false; } - var specifiedName = generateNullablesProperty ? Name + "Value" : Name; + var specifiedName = generateNullablesProperty ? Name + Value : Name; CodeMemberField specifiedMember = null; if (generateSpecifiedProperty) { - specifiedMember = new CodeMemberField(typeof(bool), specifiedName + "Specified { get; set; }"); + specifiedMember = new CodeMemberField(typeof(bool), specifiedName + Specified + GetSet); specifiedMember.CustomAttributes.Add(ignoreAttribute); if (Configuration.EntityFramework && generateNullablesProperty) { specifiedMember.CustomAttributes.Add(notMappedAttribute); } specifiedMember.Attributes = MemberAttributes.Public; - var specifiedDocs = new[] { new DocumentationModel { Language = "en", Text = string.Format("Gets or sets a value indicating whether the {0} property is specified.", Name) }, - new DocumentationModel { Language = "de", Text = string.Format("Ruft einen Wert ab, der angibt, ob die {0}-Eigenschaft spezifiziert ist, oder legt diesen fest.", Name) } }; - specifiedMember.Comments.AddRange(DocumentationModel.GetComments(specifiedDocs, Configuration).ToArray()); + var specifiedDocs = new DocumentationModel[] { + new() { Language = English, Text = $"Gets or sets a value indicating whether the {Name} property is specified." }, + new() { Language = German, Text = $"Ruft einen Wert ab, der angibt, ob die {Name}-Eigenschaft spezifiziert ist, oder legt diesen fest." } + }; + specifiedMember.Comments.AddRange(GetComments(specifiedDocs).ToArray()); typeDeclaration.Members.Add(specifiedMember); - var specifiedMemberPropertyModel = new PropertyModel(Configuration) - { - Name = specifiedName + "Specified" - }; + var specifiedMemberPropertyModel = new PropertyModel(Configuration) { Name = specifiedName + Specified }; Configuration.MemberVisitor(specifiedMember, specifiedMemberPropertyModel); } if (generateNullablesProperty) { - var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); - nullableType.TypeArguments.Add(typeReference); var nullableMember = new CodeMemberProperty { - Type = nullableType, + Type = NullableTypeRef(typeReference), Name = Name, HasSet = true, HasGet = true, @@ -1044,16 +816,16 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi nullableMember.CustomAttributes.Add(ignoreAttribute); nullableMember.Comments.AddRange(member.Comments); - var specifiedExpression = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), specifiedName + "Specified"); - var valueExpression = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), Name + "Value"); + var specifiedExpression = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), specifiedName + Specified); + var valueExpression = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), Name + Value); var conditionStatement = new CodeConditionStatement(specifiedExpression, new CodeStatement[] { new CodeMethodReturnStatement(valueExpression) }, new CodeStatement[] { new CodeMethodReturnStatement(new CodePrimitiveExpression(null)) }); nullableMember.GetStatements.Add(conditionStatement); - var getValueOrDefaultExpression = new CodeMethodInvokeExpression(new CodePropertySetValueReferenceExpression(), "GetValueOrDefault"); + var getValueOrDefaultExpression = new CodeMethodInvokeExpression(new CodePropertySetValueReferenceExpression(), nameof(Nullable.GetValueOrDefault)); var setValueStatement = new CodeAssignStatement(valueExpression, getValueOrDefaultExpression); - var hasValueExpression = new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), "HasValue"); + var hasValueExpression = new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), HasValue); var setSpecifiedStatement = new CodeAssignStatement(specifiedExpression, hasValueExpression); var statements = new List(); @@ -1062,20 +834,20 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi var ifNotEquals = new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeBinaryOperatorExpression( - new CodeMethodInvokeExpression(valueExpression, "Equals", getValueOrDefaultExpression), + new CodeMethodInvokeExpression(valueExpression, EqualsMethod, getValueOrDefaultExpression), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false) ), CodeBinaryOperatorType.BooleanOr, new CodeBinaryOperatorExpression( - new CodeMethodInvokeExpression(specifiedExpression, "Equals", hasValueExpression), + new CodeMethodInvokeExpression(specifiedExpression, EqualsMethod, hasValueExpression), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false) ) ), setValueStatement, setSpecifiedStatement, - new CodeExpressionStatement(new CodeMethodInvokeExpression(null, "OnPropertyChanged", + new CodeExpressionStatement(new CodeMethodInvokeExpression(null, OnPropertyChanged, new CodePrimitiveExpression(Name))) ); statements.Add(ifNotEquals); @@ -1090,8 +862,8 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi typeDeclaration.Members.Add(nullableMember); - var editorBrowsableAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(EditorBrowsableAttribute), Configuration)); - editorBrowsableAttribute.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(CodeUtilities.CreateTypeReference(typeof(EditorBrowsableState), Configuration)), "Never"))); + var editorBrowsableAttribute = AttributeDecl(); + editorBrowsableAttribute.Arguments.Add(new(new CodeFieldReferenceExpression(TypeRefExpr(), nameof(EditorBrowsableState.Never)))); specifiedMember?.CustomAttributes.Add(editorBrowsableAttribute); member.CustomAttributes.Add(editorBrowsableAttribute); if (Configuration.EntityFramework) { member.CustomAttributes.Add(notMappedAttribute); } @@ -1103,8 +875,8 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi { var specifiedProperty = new CodeMemberProperty { - Type = CodeUtilities.CreateTypeReference(typeof(bool), Configuration), - Name = Name + "Specified", + Type = TypeRef(), + Name = Name + Specified, HasSet = false, HasGet = true, }; @@ -1114,7 +886,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi var listReference = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), Name); var collectionType = Configuration.CollectionImplementationType ?? Configuration.CollectionType; - var countProperty = collectionType == typeof(System.Array) ? "Length" : "Count"; + var countProperty = collectionType == typeof(Array) ? nameof(Array.Length) : nameof(List.Count); var countReference = new CodePropertyReferenceExpression(listReference, countProperty); var notZeroExpression = new CodeBinaryOperatorExpression(countReference, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(0)); if (Configuration.CollectionSettersMode is CollectionSettersMode.PublicWithoutConstructorInitialization or CollectionSettersMode.Public) @@ -1125,19 +897,21 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi var returnStatement = new CodeMethodReturnStatement(notZeroExpression); specifiedProperty.GetStatements.Add(returnStatement); - var specifiedDocs = new[] { new DocumentationModel { Language = "en", Text = string.Format("Gets a value indicating whether the {0} collection is empty.", Name) }, - new DocumentationModel { Language = "de", Text = string.Format("Ruft einen Wert ab, der angibt, ob die {0}-Collection leer ist.", Name) } }; - specifiedProperty.Comments.AddRange(DocumentationModel.GetComments(specifiedDocs, Configuration).ToArray()); + var specifiedDocs = new DocumentationModel[] { + new() { Language = English, Text = $"Gets a value indicating whether the {Name} collection is empty." }, + new() { Language = German, Text = $"Ruft einen Wert ab, der angibt, ob die {Name}-Collection leer ist." } + }; + specifiedProperty.Comments.AddRange(GetComments(specifiedDocs).ToArray()); Configuration.MemberVisitor(specifiedProperty, this); typeDeclaration.Members.Add(specifiedProperty); } - if (isNullableReferenceType && Configuration.EnableNullableReferenceAttributes) + if (!IsCollection && isNullableReferenceType && Configuration.EnableNullableReferenceAttributes) { - member.CustomAttributes.Add(new CodeAttributeDeclaration("System.Diagnostics.CodeAnalysis.AllowNullAttribute")); - member.CustomAttributes.Add(new CodeAttributeDeclaration("System.Diagnostics.CodeAnalysis.MaybeNullAttribute")); + member.CustomAttributes.Add(new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(Attributes.AllowNull, Configuration))); + member.CustomAttributes.Add(new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(Attributes.MaybeNull, Configuration))); } var attributes = GetAttributes(isArray).ToArray(); @@ -1151,23 +925,25 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi if (constructor == null) { constructor = new CodeConstructor { Attributes = MemberAttributes.Public | MemberAttributes.Final }; - var constructorDocs = new[] { new DocumentationModel { Language = "en", Text = string.Format(@"Initializes a new instance of the class.", typeDeclaration.Name) }, - new DocumentationModel { Language = "de", Text = string.Format(@"Initialisiert eine neue Instanz der Klasse.", typeDeclaration.Name) } }; - constructor.Comments.AddRange(DocumentationModel.GetComments(constructorDocs, Configuration).ToArray()); + var constructorDocs = new DocumentationModel[] { + new() { Language = English, Text = $@"Initializes a new instance of the class." }, + new() { Language = German, Text = $@"Initialisiert eine neue Instanz der Klasse." } + }; + constructor.Comments.AddRange(GetComments(constructorDocs).ToArray()); typeDeclaration.Members.Add(constructor); } - var listReference = requiresBackingField ? (CodeExpression)new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), backingField.Name) : - new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), Name); + CodeExpression listReference = backingField != null + ? new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), backingField.Name) + : new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), Name); var collectionType = Configuration.CollectionImplementationType ?? Configuration.CollectionType; CodeExpression initExpression; - if (collectionType == typeof(System.Array)) + if (collectionType == typeof(Array)) { var initTypeReference = propertyType.GetReferenceFor(OwningType.Namespace, collection: false, forInit: true, attribute: IsAttribute); - var arrayReference = CodeUtilities.CreateTypeReference(typeof(System.Array), Configuration); - initExpression = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(arrayReference), "Empty", initTypeReference)); + initExpression = new CodeMethodInvokeExpression(new(TypeRefExpr(), nameof(Array.Empty), initTypeReference)); } else { @@ -1181,29 +957,24 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi if (isArray) { var arrayItemProperty = typeClassModel.Properties[0]; - var propertyAttributes = arrayItemProperty.GetAttributes(false, OwningType).ToList(); + // HACK: repackage as ArrayItemAttribute - foreach (var propertyAttribute in propertyAttributes) + foreach (var propertyAttribute in arrayItemProperty.GetAttributes(false, OwningType).ToList()) { - var arrayItemAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlArrayItemAttribute), Configuration), - propertyAttribute.Arguments.Cast().Where(x => !string.Equals(x.Name, "Order", StringComparison.Ordinal)).ToArray()); - var namespacePresent = arrayItemAttribute.Arguments.OfType().Any(a => a.Name == "Namespace"); + var arrayItemAttribute = AttributeDecl( + propertyAttribute.Arguments.Cast().Where(x => !string.Equals(x.Name, nameof(Order), StringComparison.Ordinal)).ToArray()); + var namespacePresent = arrayItemAttribute.Arguments.OfType().Any(a => a.Name == Namespace); if (!namespacePresent && !arrayItemProperty.XmlSchemaName.IsEmpty && !string.IsNullOrEmpty(arrayItemProperty.XmlSchemaName.Namespace)) - arrayItemAttribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(arrayItemProperty.XmlSchemaName.Namespace))); + arrayItemAttribute.Arguments.Add(new(Namespace, new CodePrimitiveExpression(arrayItemProperty.XmlSchemaName.Namespace))); member.CustomAttributes.Add(arrayItemAttribute); } } if (IsKey) - { - var keyAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "KeyAttribute", Configuration)); - member.CustomAttributes.Add(keyAttribute); - } + member.CustomAttributes.Add(new(CodeUtilities.CreateTypeReference(Attributes.Key, Configuration))); if (IsAny && Configuration.EntityFramework) - { member.CustomAttributes.Add(notMappedAttribute); - } Configuration.MemberVisitor(member, this); } @@ -1214,7 +985,7 @@ private IEnumerable GetAttributes(bool isArray, TypeMo if (IsKey && XmlSchemaName == null) { - attributes.Add(new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlIgnoreAttribute), Configuration))); + attributes.Add(AttributeDecl()); return attributes; } @@ -1222,101 +993,82 @@ private IEnumerable GetAttributes(bool isArray, TypeMo { if (IsAny) { - var anyAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlAnyAttributeAttribute), Configuration)); + var anyAttribute = AttributeDecl(); if (Order != null) - anyAttribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(Order.Value))); + anyAttribute.Arguments.Add(new(nameof(Order), new CodePrimitiveExpression(Order.Value))); attributes.Add(anyAttribute); } else { - attributes.Add(new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlAttributeAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name)))); + attributes.Add(AttributeDecl(new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name)))); } } else if (!isArray) { if (IsAny) { - var anyAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlAnyElementAttribute), Configuration)); + var anyAttribute = AttributeDecl(); if (Order != null) - anyAttribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(Order.Value))); + anyAttribute.Arguments.Add(new(nameof(Order), new CodePrimitiveExpression(Order.Value))); attributes.Add(anyAttribute); } else { - if (!Configuration.SeparateSubstitutes && Substitutes.Any()) + if (!Configuration.SeparateSubstitutes && Substitutes.Count > 0) { owningType ??= OwningType; foreach (var substitute in Substitutes) { - var substitutedAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlElementAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(substitute.Element.QualifiedName.Name)), - new CodeAttributeArgument("Type", new CodeTypeOfExpression(substitute.Type.GetReferenceFor(owningType.Namespace))), - new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(substitute.Element.QualifiedName.Namespace))); + var substitutedAttribute = AttributeDecl( + new(new CodePrimitiveExpression(substitute.Element.QualifiedName.Name)), + new(nameof(XmlElementAttribute.Type), new CodeTypeOfExpression(substitute.Type.GetReferenceFor(owningType.Namespace))), + new(nameof(XmlElementAttribute.Namespace), new CodePrimitiveExpression(substitute.Element.QualifiedName.Namespace))); if (Order != null) - { - substitutedAttribute.Arguments.Add(new CodeAttributeArgument("Order", - new CodePrimitiveExpression(Order.Value))); - } + substitutedAttribute.Arguments.Add(new(nameof(Order), new CodePrimitiveExpression(Order.Value))); attributes.Add(substitutedAttribute); } } - var attribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlElementAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name))); + var attribute = AttributeDecl(new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name))); if (Order != null) - { - attribute.Arguments.Add(new CodeAttributeArgument("Order", - new CodePrimitiveExpression(Order.Value))); - } + attribute.Arguments.Add(new(nameof(Order), new CodePrimitiveExpression(Order.Value))); attributes.Add(attribute); } } else { - var arrayAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlArrayAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name))); + var arrayAttribute = AttributeDecl(new CodeAttributeArgument(new CodePrimitiveExpression(XmlSchemaName.Name))); if (Order != null) - arrayAttribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(Order.Value))); + arrayAttribute.Arguments.Add(new(nameof(Order), new CodePrimitiveExpression(Order.Value))); attributes.Add(arrayAttribute); } foreach (var args in attributes.Select(a => a.Arguments)) { - bool namespacePrecalculated = args.OfType().Any(a => a.Name == "Namespace"); + bool namespacePrecalculated = args.OfType().Any(a => a.Name == Namespace); if (!namespacePrecalculated) { if (XmlNamespace != null) - { - args.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(XmlNamespace))); - } + args.Add(new(Namespace, new CodePrimitiveExpression(XmlNamespace))); if (Form == XmlSchemaForm.Qualified && IsAttribute) { if (XmlNamespace == null) - { - args.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(OwningType.XmlSchemaName.Namespace))); - } + args.Add(new(Namespace, new CodePrimitiveExpression(OwningType.XmlSchemaName.Namespace))); - args.Add(new CodeAttributeArgument("Form", - new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(CodeUtilities.CreateTypeReference(typeof(XmlSchemaForm), Configuration)), - "Qualified"))); + args.Add(new(nameof(Form), new CodeFieldReferenceExpression(TypeRefExpr(), nameof(XmlSchemaForm.Qualified)))); } else if ((Form == XmlSchemaForm.Unqualified || Form == XmlSchemaForm.None) && !IsAttribute && !IsAny && XmlNamespace == null) { - args.Add(new CodeAttributeArgument("Form", - new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(CodeUtilities.CreateTypeReference(typeof(XmlSchemaForm), Configuration)), - "Unqualified"))); + args.Add(new(nameof(Form), new CodeFieldReferenceExpression(TypeRefExpr(), nameof(XmlSchemaForm.Unqualified)))); } } if (IsNillable && !(IsCollection && Type is SimpleModel m && m.ValueType.IsValueType) && !(IsNullable && Configuration.DoNotForceIsNullable)) - { - args.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression(true))); - } + args.Add(new("IsNullable", new CodePrimitiveExpression(true))); if (Type is SimpleModel simpleModel && simpleModel.UseDataTypeAttribute) { @@ -1327,12 +1079,13 @@ private IEnumerable GetAttributes(bool isArray, TypeMo var name = xmlSchemaType.GetQualifiedName(); if (name.Namespace == XmlSchema.Namespace && name.Name != "anySimpleType") { - var dataType = new CodeAttributeArgument("DataType", new CodePrimitiveExpression(name.Name)); - args.Add(dataType); + args.Add(new("DataType", new CodePrimitiveExpression(name.Name))); break; } else + { xmlSchemaType = xmlSchemaType.BaseXmlSchemaType; + } } } } @@ -1346,24 +1099,14 @@ public class EnumValueModel public string Name { get; set; } public string Value { get; set; } public bool IsDeprecated { get; set; } - public List Documentation { get; private set; } - - public EnumValueModel() - { - Documentation = new List(); - } + public List Documentation { get; } = new(); } public class EnumModel : TypeModel { - public List Values { get; set; } - - public EnumModel(GeneratorConfiguration configuration) - : base(configuration) - { - Values = new List(); - } - + public List Values { get; set; } = new(); + + public EnumModel(GeneratorConfiguration configuration) : base(configuration) { } public override CodeTypeDeclaration Generate() { var enumDeclaration = base.Generate(); @@ -1382,12 +1125,11 @@ public override CodeTypeDeclaration Generate() var member = new CodeMemberField { Name = val.Name }; var docs = new List(val.Documentation); - DocumentationModel.AddDescription(member.CustomAttributes, docs, Configuration); + AddDescription(member.CustomAttributes, docs); if (val.Name != val.Value) // illegal identifier chars in value { - var enumAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlEnumAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(val.Value))); + var enumAttribute = AttributeDecl(new CodeAttributeArgument(new CodePrimitiveExpression(val.Value))); member.CustomAttributes.Add(enumAttribute); } @@ -1395,20 +1137,20 @@ public override CodeTypeDeclaration Generate() { // From .NET 3.5 XmlSerializer doesn't serialize objects with [Obsolete] >( - var obsolete = new DocumentationModel { Language = "en", Text = "[Obsolete]" }; + DocumentationModel obsolete = new() { Language = English, Text = "[Obsolete]" }; docs.Add(obsolete); } - member.Comments.AddRange(DocumentationModel.GetComments(docs, Configuration).ToArray()); + member.Comments.AddRange(GetComments(docs).ToArray()); enumDeclaration.Members.Add(member); } if (RootElementName != null) { - var rootAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(typeof(XmlRootAttribute), Configuration), - new CodeAttributeArgument(new CodePrimitiveExpression(RootElementName.Name)), - new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(RootElementName.Namespace))); + var rootAttribute = AttributeDecl( + new(new CodePrimitiveExpression(RootElementName.Name)), + new(nameof(XmlRootAttribute.Namespace), new CodePrimitiveExpression(RootElementName.Namespace))); enumDeclaration.CustomAttributes.Add(rootAttribute); } Configuration.TypeVisitor(enumDeclaration, this); @@ -1425,15 +1167,10 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att public class SimpleModel : TypeModel { public Type ValueType { get; set; } - public List Restrictions { get; private set; } - public bool UseDataTypeAttribute { get; set; } + public List Restrictions { get; } = new(); + public bool UseDataTypeAttribute { get; set; } = true; - public SimpleModel(GeneratorConfiguration configuration) - : base(configuration) - { - Restrictions = new List(); - UseDataTypeAttribute = true; - } + public SimpleModel(GeneratorConfiguration configuration) : base(configuration) { } public static string GetCollectionDefinitionName(string typeName, GeneratorConfiguration configuration) { @@ -1452,8 +1189,10 @@ public static string GetCollectionImplementationName(string typeName, GeneratorC private static string GetFullTypeName(string typeName, CodeTypeReference typeRef, Type type) { if (type.IsGenericTypeDefinition) + { typeRef.TypeArguments.Add(typeName); - else if (type == typeof(System.Array)) + } + else if (type == typeof(Array)) { typeRef.ArrayElementType = new CodeTypeReference(typeName); typeRef.ArrayRank = 1; @@ -1466,8 +1205,7 @@ private static string GetFullTypeName(string typeName, CodeTypeReference typeRef CSharpProvider.GenerateCodeFromExpression(typeOfExpr, writer, new CodeGeneratorOptions()); var fullTypeName = writer.ToString(); Debug.Assert(fullTypeName.StartsWith("typeof(") && fullTypeName.EndsWith(")")); - fullTypeName = fullTypeName.Substring(7, fullTypeName.Length - 8); - return fullTypeName; + return fullTypeName.Substring(7, fullTypeName.Length - 8); } public override CodeTypeDeclaration Generate() @@ -1494,12 +1232,8 @@ public override CodeTypeReference GetReferenceFor(NamespaceModel referencingName { var collectionType = forInit ? (Configuration.CollectionImplementationType ?? Configuration.CollectionType) : Configuration.CollectionType; - if (collectionType.IsGenericType) - type = collectionType.MakeGenericType(type); - else if (collectionType == typeof(System.Array)) - type = type.MakeArrayType(); - else - type = collectionType; + type = collectionType.IsGenericType ? collectionType.MakeGenericType(type) + : collectionType == typeof(Array) ? type.MakeArrayType() : collectionType; } return CodeUtilities.CreateTypeReference(type, Configuration); @@ -1528,24 +1262,15 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att } else if (type == typeof(DateTime)) { - var rv = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(CodeUtilities.CreateTypeReference(typeof(DateTime), Configuration)), - "Parse", new CodePrimitiveExpression(defaultString)); - return rv; + return new CodeMethodInvokeExpression(TypeRefExpr(), nameof(DateTime.Parse), new CodePrimitiveExpression(defaultString)); } else if (type == typeof(TimeSpan)) { - var rv = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(CodeUtilities.CreateTypeReference(typeof(XmlConvert), Configuration)), - "ToTimeSpan", new CodePrimitiveExpression(defaultString)); - return rv; + return new CodeMethodInvokeExpression(TypeRefExpr(), nameof(XmlConvert.ToTimeSpan), new CodePrimitiveExpression(defaultString)); } else if (type == typeof(bool) && !string.IsNullOrWhiteSpace(defaultString)) { - if (defaultString == "0") - return new CodePrimitiveExpression(false); - else if (defaultString == "1") - return new CodePrimitiveExpression(true); - else - return new CodePrimitiveExpression(Convert.ChangeType(defaultString, ValueType)); + return new CodePrimitiveExpression(defaultString == "0" ? false : defaultString == "1" ? true : Convert.ChangeType(defaultString, ValueType)); } else if (type == typeof(byte[]) && defaultString != null) { @@ -1556,8 +1281,7 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att // For whatever reason, CodeDom will not generate a semicolon for the assignment statement if CodeArrayCreateExpression // is used alone. Casting the value to the same type to work around this issue. - var rv = new CodeCastExpression(typeof(byte[]), new CodeArrayCreateExpression(typeof(byte), byteValues)); - return rv; + return new CodeCastExpression(typeof(byte[]), new CodeArrayCreateExpression(typeof(byte), byteValues)); } else if (type == typeof(double) && !string.IsNullOrWhiteSpace(defaultString)) { @@ -1573,9 +1297,7 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att public IEnumerable GetRestrictionAttributes() { foreach (var attribute in Restrictions.Where(x => x.IsSupported).Select(r => r.GetAttribute()).Where(a => a != null)) - { yield return attribute; - } var minInclusive = Restrictions.OfType().FirstOrDefault(x => x.IsSupported); var maxInclusive = Restrictions.OfType().FirstOrDefault(x => x.IsSupported); @@ -1583,27 +1305,95 @@ public IEnumerable GetRestrictionAttributes() if (minInclusive != null && maxInclusive != null) { var rangeAttribute = new CodeAttributeDeclaration( - CodeUtilities.CreateTypeReference("System.ComponentModel.DataAnnotations", "RangeAttribute", Configuration), - new CodeAttributeArgument(new CodeTypeOfExpression(minInclusive.Type)), - new CodeAttributeArgument(new CodePrimitiveExpression(minInclusive.Value)), - new CodeAttributeArgument(new CodePrimitiveExpression(maxInclusive.Value))); + CodeUtilities.CreateTypeReference(Attributes.Range, Configuration), + new(new CodeTypeOfExpression(minInclusive.Type)), + new(new CodePrimitiveExpression(minInclusive.Value)), + new(new CodePrimitiveExpression(maxInclusive.Value))); // see https://github.com/mganss/XmlSchemaClassGenerator/issues/268 if (Configuration.NetCoreSpecificCode) { if (minInclusive.Value.Contains(".") || maxInclusive.Value.Contains(".")) - { - rangeAttribute.Arguments.Add(new CodeAttributeArgument("ParseLimitsInInvariantCulture", new CodePrimitiveExpression(true))); - } + rangeAttribute.Arguments.Add(new("ParseLimitsInInvariantCulture", new CodePrimitiveExpression(true))); if (minInclusive.Type != typeof(int) && minInclusive.Type != typeof(double)) - { - rangeAttribute.Arguments.Add(new CodeAttributeArgument("ConvertValueInInvariantCulture", new CodePrimitiveExpression(true))); - } + rangeAttribute.Arguments.Add(new("ConvertValueInInvariantCulture", new CodePrimitiveExpression(true))); } yield return rangeAttribute; } } } + + public class GeneratorModel + { + protected const string OnPropertyChanged = nameof(OnPropertyChanged); + protected const string EqualsMethod = nameof(object.Equals); + protected const string HasValue = nameof(Nullable.HasValue); + + protected const string GetSet = " { get; set; }"; + + protected const string English = "en"; + protected const string German = "de"; + + protected GeneratorModel(GeneratorConfiguration configuration) => Configuration = configuration; + + public GeneratorConfiguration Configuration { get; } + + protected CodeTypeReferenceExpression TypeRefExpr() => new(TypeRef()); + + protected CodeAttributeDeclaration AttributeDecl(params CodeAttributeArgument[] args) => new(TypeRef(), args); + + private protected CodeAttributeDeclaration AttributeDecl(TypeInfo attribute, CodeAttributeArgument arg) + => new(CodeUtilities.CreateTypeReference(attribute, Configuration), arg); + + protected CodeTypeReference TypeRef() => CodeUtilities.CreateTypeReference(typeof(T), Configuration); + + protected CodeTypeReference NullableTypeRef(CodeTypeReference typeReference) + { + var nullableType = CodeUtilities.CreateTypeReference(typeof(Nullable<>), Configuration); + nullableType.TypeArguments.Add(typeReference); + return nullableType; + } + public static bool DisableComments { get; set; } + + protected IEnumerable GetComments(IList docs) + { + if (DisableComments || docs.Count == 0) + yield break; + + yield return new CodeCommentStatement("", true); + + foreach (var doc in docs + .Where(d => string.IsNullOrEmpty(d.Language) || Configuration.CommentLanguages.Any(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase))) + .OrderBy(d => d.Language)) + { + var text = doc.Text; + var comment = $"{CodeUtilities.NormalizeNewlines(text).Trim()}"; + yield return new CodeCommentStatement(comment, true); + } + + yield return new CodeCommentStatement("", true); + } + + protected void AddDescription(CodeAttributeDeclarationCollection attributes, IEnumerable docs) + { + if (!Configuration.GenerateDescriptionAttribute || DisableComments || !docs.Any()) return; + + var doc = GetSingleDoc(docs.Where(d => string.IsNullOrEmpty(d.Language) || Configuration.CommentLanguages.Any(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase)))); + + if (doc != null) + { + var descriptionAttribute = AttributeDecl(new CodeAttributeArgument(new CodePrimitiveExpression(Regex.Replace(doc.Text, @"\s+", " ").Trim()))); + attributes.Add(descriptionAttribute); + } + } + + private static DocumentationModel GetSingleDoc(IEnumerable docs) + { + return docs.Count() == 1 ? docs.Single() + : docs.FirstOrDefault(d => string.IsNullOrEmpty(d.Language) || d.Language.StartsWith(English, StringComparison.OrdinalIgnoreCase)) + ?? docs.FirstOrDefault(); + } + } } From 6e3fa572910f7faa4ad9871d5cb0211908503fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 2 Jun 2022 09:32:08 +0200 Subject: [PATCH 2/4] Fix FileOutputWriterTests on non-Windows platforms All tests would fail because paths are made of / instead of \ on Linux and macOS. Also the "Confiruation" typo was fixed. --- .../FileOutputWriterTests.cs | 117 ++++++++++-------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/XmlSchemaClassGenerator.Tests/FileOutputWriterTests.cs b/XmlSchemaClassGenerator.Tests/FileOutputWriterTests.cs index ab0a92f9..15af143d 100644 --- a/XmlSchemaClassGenerator.Tests/FileOutputWriterTests.cs +++ b/XmlSchemaClassGenerator.Tests/FileOutputWriterTests.cs @@ -43,7 +43,8 @@ void Action() => Compiler.Generate( [UseCulture("en-US")] public void TestDefaultProviderGeneratorPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "DefaultProviderGeneratorPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "DefaultProviderGeneratorPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "DefaultProviderGeneratorPrefix", @@ -57,7 +58,7 @@ public void TestDefaultProviderGeneratorPrefix() SharedTestFunctions.TestSamples(_output, "DefaultProviderGeneratorPrefix", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\DefaultProviderGeneratorPrefix\Generator.Prefix.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "Generator.Prefix.cs"), outputWriter.Files.First()); } [Fact] @@ -65,7 +66,8 @@ public void TestDefaultProviderGeneratorPrefix() [UseCulture("en-US")] public void TestEmptyKeyProvider() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProvider")); + var directory = Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProvider"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "EmptyKeyProvider", @@ -82,7 +84,7 @@ public void TestEmptyKeyProvider() SharedTestFunctions.TestSamples(_output, "EmptyKeyProvider", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\EmptyKeyProvider\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] @@ -90,7 +92,8 @@ public void TestEmptyKeyProvider() [UseCulture("en-US")] public void TestEmptyKeyProviderGeneratorPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProviderGeneratorPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProviderGeneratorPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "EmptyKeyProviderGeneratorPrefix", @@ -108,18 +111,19 @@ public void TestEmptyKeyProviderGeneratorPrefix() SharedTestFunctions.TestSamples(_output, "EmptyKeyProviderGeneratorPrefix", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\EmptyKeyProviderGeneratorPrefix\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] [TestPriority(1)] [UseCulture("en-US")] - public void TestEmptyKeyProviderGeneratorConfiruationPrefix() + public void TestEmptyKeyProviderGeneratorConfigurationPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProviderGeneratorConfiruationPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProviderGeneratorConfigurationPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( - "EmptyKeyProviderGeneratorConfiruationPrefix", + "EmptyKeyProviderGeneratorConfigurationPrefix", PrefixPattern, new Generator { @@ -131,9 +135,9 @@ public void TestEmptyKeyProviderGeneratorConfiruationPrefix() }.ToNamespaceProvider(new GeneratorConfiguration { NamespacePrefix = "GeneratorConfiguration.Prefix" }.NamespaceProvider.GenerateNamespace), }); - SharedTestFunctions.TestSamples(_output, "EmptyKeyProviderGeneratorConfiruationPrefix", PrefixPattern); + SharedTestFunctions.TestSamples(_output, "EmptyKeyProviderGeneratorConfigurationPrefix", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\EmptyKeyProviderGeneratorConfiruationPrefix\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] @@ -141,7 +145,8 @@ public void TestEmptyKeyProviderGeneratorConfiruationPrefix() [UseCulture("en-US")] public void TestEmptyKeyProviderBothPrefixes() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProviderBothPrefixes")); + var directory = Path.Combine("output", "FileOutputWriterTests", "EmptyKeyProviderBothPrefixes"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "EmptyKeyProviderBothPrefixes", @@ -159,7 +164,7 @@ public void TestEmptyKeyProviderBothPrefixes() SharedTestFunctions.TestSamples(_output, "EmptyKeyProviderBothPrefixes", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\EmptyKeyProviderBothPrefixes\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] @@ -167,7 +172,8 @@ public void TestEmptyKeyProviderBothPrefixes() [UseCulture("en-US")] public void TestFullKeyProvider() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "FullKeyProvider")); + var directory = Path.Combine("output", "FileOutputWriterTests", "FullKeyProvider"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "FullKeyProvider", @@ -184,7 +190,7 @@ public void TestFullKeyProvider() SharedTestFunctions.TestSamples(_output, "FullKeyProvider", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\FullKeyProvider\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] @@ -192,7 +198,8 @@ public void TestFullKeyProvider() [UseCulture("en-US")] public void TestFullKeyProviderGeneratorPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "FullKeyProviderGeneratorPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "FullKeyProviderGeneratorPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "FullKeyProviderGeneratorPrefix", @@ -210,18 +217,19 @@ public void TestFullKeyProviderGeneratorPrefix() SharedTestFunctions.TestSamples(_output, "FullKeyProviderGeneratorPrefix", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\FullKeyProviderGeneratorPrefix\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] [TestPriority(1)] [UseCulture("en-US")] - public void TestFullKeyProviderGeneratorConfiruationPrefix() + public void TestFullKeyProviderGeneratorConfigurationPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "FullKeyProviderGeneratorConfiruationPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "FullKeyProviderGeneratorConfigurationPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( - "FullKeyProviderGeneratorConfiruationPrefix", + "FullKeyProviderGeneratorConfigurationPrefix", PrefixPattern, new Generator { @@ -233,9 +241,9 @@ public void TestFullKeyProviderGeneratorConfiruationPrefix() }.ToNamespaceProvider(new GeneratorConfiguration { NamespacePrefix = "GeneratorConfiguration.Prefix" }.NamespaceProvider.GenerateNamespace), }); - SharedTestFunctions.TestSamples(_output, "FullKeyProviderGeneratorConfiruationPrefix", PrefixPattern); + SharedTestFunctions.TestSamples(_output, "FullKeyProviderGeneratorConfigurationPrefix", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\FullKeyProviderGeneratorConfiruationPrefix\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] @@ -243,7 +251,8 @@ public void TestFullKeyProviderGeneratorConfiruationPrefix() [UseCulture("en-US")] public void TestFullKeyProviderBothPrefixes() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "FullKeyProviderBothPrefixes")); + var directory = Path.Combine("output", "FileOutputWriterTests", "FullKeyProviderBothPrefixes"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "FullKeyProviderBothPrefixes", @@ -261,7 +270,7 @@ public void TestFullKeyProviderBothPrefixes() SharedTestFunctions.TestSamples(_output, "FullKeyProviderBothPrefixes", PrefixPattern); Assert.Single(outputWriter.Files); - Assert.Equal(@"output\FileOutputWriterTests\FullKeyProviderBothPrefixes\NamedNamespace.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace.cs"), outputWriter.Files.First()); } [Fact] @@ -269,7 +278,8 @@ public void TestFullKeyProviderBothPrefixes() [UseCulture("en-US")] public void TestSeparateDefaultProvider_ThrowsArgumentException() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateDefaultProvider")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateDefaultProvider"); + var outputWriter = new FileWatcherOutputWriter(directory); void Action() => Compiler.Generate( "SeparateDefaultProvider", @@ -290,7 +300,8 @@ void Action() => Compiler.Generate( [UseCulture("en-US")] public void TestSeparateDefaultProviderGeneratorPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateDefaultProviderGeneratorPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateDefaultProviderGeneratorPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateDefaultProviderGeneratorPrefix", @@ -305,7 +316,7 @@ public void TestSeparateDefaultProviderGeneratorPrefix() SharedTestFunctions.TestSamples(_output, "SeparateDefaultProviderGeneratorPrefix", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateDefaultProviderGeneratorPrefix\Generator.Prefix\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "Generator.Prefix", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] @@ -313,7 +324,8 @@ public void TestSeparateDefaultProviderGeneratorPrefix() [UseCulture("en-US")] public void TestSeparateEmptyKeyProvider() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProvider")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProvider"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateEmptyKeyProvider", @@ -331,7 +343,7 @@ public void TestSeparateEmptyKeyProvider() SharedTestFunctions.TestSamples(_output, "SeparateEmptyKeyProvider", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateEmptyKeyProvider\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] @@ -339,7 +351,8 @@ public void TestSeparateEmptyKeyProvider() [UseCulture("en-US")] public void TestSeparateEmptyKeyProviderGeneratorPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProviderGeneratorPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProviderGeneratorPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateEmptyKeyProviderGeneratorPrefix", @@ -358,18 +371,19 @@ public void TestSeparateEmptyKeyProviderGeneratorPrefix() SharedTestFunctions.TestSamples(_output, "SeparateEmptyKeyProviderGeneratorPrefix", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateEmptyKeyProviderGeneratorPrefix\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] [TestPriority(1)] [UseCulture("en-US")] - public void TestSeparateEmptyKeyProviderGeneratorConfiruationPrefix() + public void TestSeparateEmptyKeyProviderGeneratorConfigurationPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProviderGeneratorConfiruationPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProviderGeneratorConfigurationPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( - "SeparateEmptyKeyProviderGeneratorConfiruationPrefix", + "SeparateEmptyKeyProviderGeneratorConfigurationPrefix", PrefixPattern, new Generator { @@ -382,9 +396,9 @@ public void TestSeparateEmptyKeyProviderGeneratorConfiruationPrefix() }.ToNamespaceProvider(new GeneratorConfiguration { NamespacePrefix = "GeneratorConfiguration.Prefix" }.NamespaceProvider.GenerateNamespace), }); - SharedTestFunctions.TestSamples(_output, "SeparateEmptyKeyProviderGeneratorConfiruationPrefix", PrefixPattern); + SharedTestFunctions.TestSamples(_output, "SeparateEmptyKeyProviderGeneratorConfigurationPrefix", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateEmptyKeyProviderGeneratorConfiruationPrefix\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] @@ -392,7 +406,8 @@ public void TestSeparateEmptyKeyProviderGeneratorConfiruationPrefix() [UseCulture("en-US")] public void TestSeparateEmptyKeyProviderBothPrefixes() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProviderBothPrefixes")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateEmptyKeyProviderBothPrefixes"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateEmptyKeyProviderBothPrefixes", @@ -411,7 +426,7 @@ public void TestSeparateEmptyKeyProviderBothPrefixes() SharedTestFunctions.TestSamples(_output, "SeparateEmptyKeyProviderBothPrefixes", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateEmptyKeyProviderBothPrefixes\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] @@ -419,7 +434,8 @@ public void TestSeparateEmptyKeyProviderBothPrefixes() [UseCulture("en-US")] public void TestSeparateFullKeyProvider() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProvider")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProvider"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateFullKeyProvider", @@ -437,7 +453,7 @@ public void TestSeparateFullKeyProvider() SharedTestFunctions.TestSamples(_output, "SeparateFullKeyProvider", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateFullKeyProvider\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] @@ -445,7 +461,8 @@ public void TestSeparateFullKeyProvider() [UseCulture("en-US")] public void TestSeparateFullKeyProviderGeneratorPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProviderGeneratorPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProviderGeneratorPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateFullKeyProviderGeneratorPrefix", @@ -464,18 +481,19 @@ public void TestSeparateFullKeyProviderGeneratorPrefix() SharedTestFunctions.TestSamples(_output, "SeparateFullKeyProviderGeneratorPrefix", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateFullKeyProviderGeneratorPrefix\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] [TestPriority(1)] [UseCulture("en-US")] - public void TestSeparateFullKeyProviderGeneratorConfiruationPrefix() + public void TestSeparateFullKeyProviderGeneratorConfigurationPrefix() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProviderGeneratorConfiruationPrefix")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProviderGeneratorConfigurationPrefix"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( - "SeparateFullKeyProviderGeneratorConfiruationPrefix", + "SeparateFullKeyProviderGeneratorConfigurationPrefix", PrefixPattern, new Generator { @@ -488,9 +506,9 @@ public void TestSeparateFullKeyProviderGeneratorConfiruationPrefix() }.ToNamespaceProvider(new GeneratorConfiguration { NamespacePrefix = "GeneratorConfiguration.Prefix" }.NamespaceProvider.GenerateNamespace), }); - SharedTestFunctions.TestSamples(_output, "SeparateFullKeyProviderGeneratorConfiruationPrefix", PrefixPattern); + SharedTestFunctions.TestSamples(_output, "SeparateFullKeyProviderGeneratorConfigurationPrefix", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateFullKeyProviderGeneratorConfiruationPrefix\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } [Fact] @@ -498,7 +516,8 @@ public void TestSeparateFullKeyProviderGeneratorConfiruationPrefix() [UseCulture("en-US")] public void TestSeparateFullKeyProviderBothPrefixes() { - var outputWriter = new FileWatcherOutputWriter(Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProviderBothPrefixes")); + var directory = Path.Combine("output", "FileOutputWriterTests", "SeparateFullKeyProviderBothPrefixes"); + var outputWriter = new FileWatcherOutputWriter(directory); Compiler.Generate( "SeparateFullKeyProviderBothPrefixes", @@ -517,7 +536,7 @@ public void TestSeparateFullKeyProviderBothPrefixes() SharedTestFunctions.TestSamples(_output, "SeparateFullKeyProviderBothPrefixes", PrefixPattern); Assert.Equal(2, outputWriter.Files.Count()); - Assert.Equal(@"output\FileOutputWriterTests\SeparateFullKeyProviderBothPrefixes\NamedNamespace\PurchaseOrderType.cs", outputWriter.Files.First()); + Assert.Equal(Path.Combine(directory, "NamedNamespace", "PurchaseOrderType.cs"), outputWriter.Files.First()); } } } From b474f8ba7bf84232e3e3ad1badaef46422d39664 Mon Sep 17 00:00:00 2001 From: Michael Ganss Date: Thu, 2 Jun 2022 14:07:26 +0200 Subject: [PATCH 3/4] Add support for NRTs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 95179c37..f58f996d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ from schema restrictions * Optional support for Entity Framework Code First (automatically generate key properties) * Optionally generate interfaces for groups and attribute groups * Optionally generate one file per class +* Support for nullable reference types (NRTs) through [`AllowNullAttribute`](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.allownullattribute) and [`MaybeNullAttribute`](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.maybenullattribute) Unsupported: From cf0d2663d32ca3c59c799a1499c3b3713ce5a4af Mon Sep 17 00:00:00 2001 From: Michael Ganss Date: Tue, 7 Jun 2022 12:18:06 +0200 Subject: [PATCH 4/4] Prevent stack overflow (fixes #329) --- .../xsd/simple/recursive.xsd | 49 +++++++++++++++++++ XmlSchemaClassGenerator/ModelBuilder.cs | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 XmlSchemaClassGenerator.Tests/xsd/simple/recursive.xsd diff --git a/XmlSchemaClassGenerator.Tests/xsd/simple/recursive.xsd b/XmlSchemaClassGenerator.Tests/xsd/simple/recursive.xsd new file mode 100644 index 00000000..11832ad8 --- /dev/null +++ b/XmlSchemaClassGenerator.Tests/xsd/simple/recursive.xsd @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs index 5db02dc8..b6b81073 100644 --- a/XmlSchemaClassGenerator/ModelBuilder.cs +++ b/XmlSchemaClassGenerator/ModelBuilder.cs @@ -380,7 +380,7 @@ private IEnumerable GetSubstitutedElements(XmlQualifiedName name) { if (SubstitutionGroups.TryGetValue(name, out var substitutes)) { - foreach (var substitute in substitutes) + foreach (var substitute in substitutes.Where(s => s.Element.QualifiedName != name)) { yield return substitute; foreach (var recursiveSubstitute in GetSubstitutedElements(substitute.Element.QualifiedName))