Skip to content

Commit

Permalink
Merge pull request #189 from AVTit/master
Browse files Browse the repository at this point in the history
Implement public property setter generation for collection fields
  • Loading branch information
mganss authored Apr 29, 2020
2 parents 09d37b6 + 99c6229 commit 5bfc2fc
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 4 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ Options:
--cit, --collectionImplementationType=VALUE
the default collection type implementation to use (
default is null)
--csm, --collectionSettersMode=Private, Public, PublicWithoutConstructorInitialization
generate a private, public or public setters
without backing field initialization for collections
(default is Private; can be: Private, Public,
PublicWithoutConstructorInitialization)
--ctro, --codeTypeReferenceOptions=GlobalReference, GenericTypeParameter
the default CodeTypeReferenceOptions Flags to use (
default is unset; can be: GlobalReference,
Expand Down
28 changes: 27 additions & 1 deletion XmlSchemaClassGenerator.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static void Main(string[] args)
var generateComplexTypesForCollections = true;
var useShouldSerialize = false;
var separateClasses = false;
var collectionSettersMode = CollectionSettersMode.Private;

var options = new OptionSet {
{ "h|help", "show this message and exit", v => showHelp = v != null },
Expand Down Expand Up @@ -84,6 +85,30 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
{ "u|enableUpaCheck", "should XmlSchemaSet check for Unique Particle Attribution (UPA) (default is enabled)", v => enableUpaCheck = v != null },
{ "ct|collectionType=", "collection type to use (default is " + typeof(Collection<>).FullName + ")", v => collectionType = v == null ? typeof(Collection<>) : Type.GetType(v, true) },
{ "cit|collectionImplementationType=", "the default collection type implementation to use (default is null)", v => collectionImplementationType = v == null ? null : Type.GetType(v, true) },
{ "csm|collectionSettersMode=", @"generate a private, public or public setters
without backing field initialization for collections
(default is Private; can be: {Private, Public, PublicWithoutConstructorInitialization})",
v =>
{
switch (v)
{
case "pr":
case "Private":
collectionSettersMode = CollectionSettersMode.Private;
break;
case "pu":
case "Public":
collectionSettersMode = CollectionSettersMode.Public;
break;
case "puwci":
case "PublicWithoutConstructorInitialization":
collectionSettersMode = CollectionSettersMode.PublicWithoutConstructorInitialization;
break;
default:
collectionSettersMode = CollectionSettersMode.Private;
break;
}
}},
{ "ctro|codeTypeReferenceOptions=", "the default CodeTypeReferenceOptions Flags to use (default is unset; can be: {GlobalReference, GenericTypeParameter})", v => codeTypeReferenceOptions = v == null ? default : (CodeTypeReferenceOptions)Enum.Parse(typeof(CodeTypeReferenceOptions), v, false) },
{ "tvpn|textValuePropertyName=", "the name of the property that holds the text value of an element (default is Value)", v => textValuePropertyName = v },
{ "dst|debuggerStepThrough", "generate DebuggerStepThroughAttribute (default is enabled)", v => generateDebuggerStepThroughAttribute = v != null },
Expand Down Expand Up @@ -159,7 +184,8 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
EnableUpaCheck = enableUpaCheck,
GenerateComplexTypesForCollections = generateComplexTypesForCollections,
UseShouldSerializePattern = useShouldSerialize,
SeparateClasses = separateClasses
SeparateClasses = separateClasses,
CollectionSettersMode = collectionSettersMode
};

if (pclCompatible)
Expand Down
1 change: 1 addition & 0 deletions XmlSchemaClassGenerator.Tests/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public static Assembly GenerateFiles(string name, IEnumerable<string> files, Gen
MemberVisitor = generatorPrototype.MemberVisitor,
GenerateDescriptionAttribute = generatorPrototype.GenerateDescriptionAttribute,
CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions,
CollectionSettersMode = generatorPrototype.CollectionSettersMode,
TextValuePropertyName = generatorPrototype.TextValuePropertyName,
EmitOrder = generatorPrototype.EmitOrder,
SeparateClasses = generatorPrototype.SeparateClasses
Expand Down
97 changes: 96 additions & 1 deletion XmlSchemaClassGenerator.Tests/XmlTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -120,6 +120,101 @@ public void TestList()
TestSamples("List", ListPattern);
}

[Fact]
public void TestListWithPrivatePropertySetters()
{
var assembly = Compiler.Generate("List", ListPattern, new Generator() {
GenerateNullables = true,
IntegerDataType = typeof(int),
DataAnnotationMode = DataAnnotationMode.All,
GenerateDesignerCategoryAttribute = false,
GenerateComplexTypesForCollections = true,
EntityFramework = false,
GenerateInterfaces = true,
NamespacePrefix = "List",
GenerateDescriptionAttribute = true,
TextValuePropertyName = "Value",
CollectionSettersMode = CollectionSettersMode.Private
});
Assert.NotNull(assembly);
var myClassType = assembly.GetType("List.MyClass");
Assert.NotNull(myClassType);
var iListType = typeof(Collection<>);
var collectionPropertyInfos = myClassType.GetProperties().Where(p => p.PropertyType.IsGenericType && iListType.IsAssignableFrom(p.PropertyType.GetGenericTypeDefinition())).OrderBy(p=>p.Name).ToList();
var publicCollectionPropertyInfos = collectionPropertyInfos.Where(p => p.SetMethod.IsPrivate).OrderBy(p=>p.Name).ToList();
Assert.True(collectionPropertyInfos.Count > 0);
Assert.Equal(collectionPropertyInfos, publicCollectionPropertyInfos);

var myClassInstance = Activator.CreateInstance(myClassType);
foreach (var collectionPropertyInfo in publicCollectionPropertyInfos)
{
Assert.NotNull(collectionPropertyInfo.GetValue(myClassInstance));
}
}

[Fact]
public void TestListWithPublicPropertySetters()
{
var assembly = Compiler.Generate("ListPublic", ListPattern, new Generator {
GenerateNullables = true,
IntegerDataType = typeof(int),
DataAnnotationMode = DataAnnotationMode.All,
GenerateDesignerCategoryAttribute = false,
GenerateComplexTypesForCollections = true,
EntityFramework = false,
GenerateInterfaces = true,
NamespacePrefix = "List",
GenerateDescriptionAttribute = true,
TextValuePropertyName = "Value",
CollectionSettersMode = CollectionSettersMode.Public
});
Assert.NotNull(assembly);
var myClassType = assembly.GetType("List.MyClass");
Assert.NotNull(myClassType);
var iListType = typeof(Collection<>);
var collectionPropertyInfos = myClassType.GetProperties().Where(p => p.PropertyType.IsGenericType && iListType.IsAssignableFrom(p.PropertyType.GetGenericTypeDefinition())).OrderBy(p=>p.Name).ToList();
var publicCollectionPropertyInfos = collectionPropertyInfos.Where(p => p.SetMethod.IsPublic).OrderBy(p=>p.Name).ToList();
Assert.True(collectionPropertyInfos.Count > 0);
Assert.Equal(collectionPropertyInfos, publicCollectionPropertyInfos);

var myClassInstance = Activator.CreateInstance(myClassType);
foreach (var collectionPropertyInfo in publicCollectionPropertyInfos)
{
Assert.NotNull(collectionPropertyInfo.GetValue(myClassInstance));
}
}

[Fact]
public void TestListWithPublicPropertySettersWithoutConstructors()
{
var assembly = Compiler.Generate("ListPublicWithoutConstructorInitialization", ListPattern, new Generator {
GenerateNullables = true,
IntegerDataType = typeof(int),
DataAnnotationMode = DataAnnotationMode.All,
GenerateDesignerCategoryAttribute = false,
GenerateComplexTypesForCollections = true,
EntityFramework = false,
GenerateInterfaces = true,
NamespacePrefix = "List",
GenerateDescriptionAttribute = true,
TextValuePropertyName = "Value",
CollectionSettersMode = CollectionSettersMode.PublicWithoutConstructorInitialization
});
Assert.NotNull(assembly);
var myClassType = assembly.GetType("List.MyClass");
Assert.NotNull(myClassType);
var iListType = typeof(Collection<>);
var collectionPropertyInfos = myClassType.GetProperties().Where(p => p.PropertyType.IsGenericType && iListType.IsAssignableFrom(p.PropertyType.GetGenericTypeDefinition())).OrderBy(p=>p.Name).ToList();
var publicCollectionPropertyInfos = collectionPropertyInfos.Where(p => p.SetMethod.IsPublic).OrderBy(p=>p.Name).ToList();
Assert.True(collectionPropertyInfos.Count > 0);
Assert.Equal(collectionPropertyInfos, publicCollectionPropertyInfos);
var myClassInstance = Activator.CreateInstance(myClassType);
foreach (var collectionPropertyInfo in publicCollectionPropertyInfos)
{
Assert.Null(collectionPropertyInfo.GetValue(myClassInstance));
}
}

[Fact, TestPriority(1)]
[UseCulture("en-US")]
public void TestSimple()
Expand Down
25 changes: 25 additions & 0 deletions XmlSchemaClassGenerator/CollectionSettersMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace XmlSchemaClassGenerator
{
/// <summary>
/// Determines the kind collection accessor modifiers to emit and controls baking collection fields initialization
/// </summary>
public enum CollectionSettersMode
{
/// <summary>
/// All collection setters are private
/// </summary>
Private,
/// <summary>
/// All collection setters are public
/// </summary>
Public,
/// <summary>
/// All collection setters are public and baking collections fields not initialized in constructors
/// </summary>
PublicWithoutConstructorInitialization
}
}
6 changes: 6 additions & 0 deletions XmlSchemaClassGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ public CodeTypeReferenceOptions CodeTypeReferenceOptions
set { _configuration.CodeTypeReferenceOptions = value; }
}

public CollectionSettersMode CollectionSettersMode
{
get { return _configuration.CollectionSettersMode; }
set { _configuration.CollectionSettersMode = value; }
}

public string TextValuePropertyName
{
get { return _configuration.TextValuePropertyName; }
Expand Down
4 changes: 4 additions & 0 deletions XmlSchemaClassGenerator/GeneratorConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public NamingScheme NamingScheme
/// Generator Code reference options
/// </summary>
public CodeTypeReferenceOptions CodeTypeReferenceOptions { get; set; }
/// <summary>
/// Determines the kind of collection accessor modifiers to emit and controls baking collection fields initialization
/// </summary>
public CollectionSettersMode CollectionSettersMode { get; set; }

/// <summary>
/// The name of the property that will contain the text value of an XML element
Expand Down
4 changes: 2 additions & 2 deletions XmlSchemaClassGenerator/TypeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi
else
member = new CodeMemberField(typeReference, propertyName);

var isPrivateSetter = IsCollection || isArray || (IsList && IsAttribute);
var isPrivateSetter = (IsCollection || isArray || (IsList && IsAttribute)) && Configuration.CollectionSettersMode == CollectionSettersMode.Private;

if (requiresBackingField)
{
Expand Down Expand Up @@ -1011,7 +1011,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi
member.CustomAttributes.AddRange(attributes);

// initialize List<>
if (IsCollection || isArray || (IsList && IsAttribute))
if ((IsCollection || isArray || (IsList && IsAttribute)) && Configuration.CollectionSettersMode != CollectionSettersMode.PublicWithoutConstructorInitialization)
{
var constructor = typeDeclaration.Members.OfType<CodeConstructor>().FirstOrDefault();
if (constructor == null)
Expand Down

0 comments on commit 5bfc2fc

Please sign in to comment.