diff --git a/XmlSchemaClassGenerator.Console/Program.cs b/XmlSchemaClassGenerator.Console/Program.cs index 7296b2c7..fd12c561 100644 --- a/XmlSchemaClassGenerator.Console/Program.cs +++ b/XmlSchemaClassGenerator.Console/Program.cs @@ -30,6 +30,7 @@ static void Main(string[] args) var entityFramework = false; var interfaces = true; var pascal = true; + var assembly = false; var collectionType = typeof(Collection<>); Type collectionImplementationType = null; var codeTypeReferenceOptions = default(CodeTypeReferenceOptions); @@ -75,6 +76,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l { "f|ef", "generate Entity Framework Code First compatible classes", v => entityFramework = v != null }, { "t|interface", "generate interfaces for groups and attribute groups (default is enabled)", v => interfaces = v != null }, { "a|pascal", "use Pascal case for class and property names (default is enabled)", v => pascal = v != null }, + { "av|assemblyVisible", "use the internal visibility modifier (default is false)", v => assembly = v != null }, { "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) }, @@ -121,6 +123,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l EntityFramework = entityFramework, GenerateInterfaces = interfaces, NamingScheme = pascal ? NamingScheme.PascalCase : NamingScheme.Direct, + AssemblyVisible=assembly, CollectionType = collectionType, CollectionImplementationType = collectionImplementationType, CodeTypeReferenceOptions = codeTypeReferenceOptions, diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index bf6bac67..b830e08d 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -41,9 +41,10 @@ private IEnumerable ConvertXml(string name, string xsd, Generator genera DataAnnotationMode = generatorPrototype.DataAnnotationMode, GenerateDesignerCategoryAttribute = generatorPrototype.GenerateDesignerCategoryAttribute, EntityFramework = generatorPrototype.EntityFramework, + AssemblyVisible = generatorPrototype.AssemblyVisible, GenerateInterfaces = generatorPrototype.GenerateInterfaces, MemberVisitor = generatorPrototype.MemberVisitor, - CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions + CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions }; var set = new XmlSchemaSet(); @@ -389,12 +390,12 @@ public void DontGenerateElementForEmptyCollectionInChoice() } - [Theory] - [InlineData(CodeTypeReferenceOptions.GlobalReference, "[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]")] - [InlineData((CodeTypeReferenceOptions)0, "[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]")] - public void EditorBrowsableAttributeRespectsCodeTypeReferenceOptions(CodeTypeReferenceOptions codeTypeReferenceOptions, string expectedLine) - { - const string xsd = @" + [Theory] + [InlineData(CodeTypeReferenceOptions.GlobalReference, "[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]")] + [InlineData((CodeTypeReferenceOptions)0, "[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]")] + public void EditorBrowsableAttributeRespectsCodeTypeReferenceOptions(CodeTypeReferenceOptions codeTypeReferenceOptions, string expectedLine) + { + const string xsd = @" @@ -409,26 +410,26 @@ public void EditorBrowsableAttributeRespectsCodeTypeReferenceOptions(CodeTypeRef "; - var generatedType = ConvertXml(nameof(EditorBrowsableAttributeRespectsCodeTypeReferenceOptions), xsd, new Generator - { - CodeTypeReferenceOptions = codeTypeReferenceOptions, - GenerateNullables = true, - GenerateInterfaces = false, - NamespaceProvider = new NamespaceProvider - { - GenerateNamespace = key => "Test" - } - }); - - Assert.Contains( - expectedLine, - generatedType.First()); - } - - [Fact] - public void MixedTypeMustNotCollideWithExistingMembers() - { - const string xsd = @" + var generatedType = ConvertXml(nameof(EditorBrowsableAttributeRespectsCodeTypeReferenceOptions), xsd, new Generator + { + CodeTypeReferenceOptions = codeTypeReferenceOptions, + GenerateNullables = true, + GenerateInterfaces = false, + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + } + }); + + Assert.Contains( + expectedLine, + generatedType.First()); + } + + [Fact] + public void MixedTypeMustNotCollideWithExistingMembers() + { + const string xsd = @" @@ -437,23 +438,23 @@ public void MixedTypeMustNotCollideWithExistingMembers() "; - var generatedType = ConvertXml(nameof(MixedTypeMustNotCollideWithExistingMembers), xsd, new Generator - { - NamespaceProvider = new NamespaceProvider - { - GenerateNamespace = key => "Test" - } - }); - - Assert.Contains( - @"public string[] Text_1 { get; set; }", - generatedType.First()); - } - - [Fact] - public void MixedTypeMustNotCollideWithContainingTypeName() - { - const string xsd = @" + var generatedType = ConvertXml(nameof(MixedTypeMustNotCollideWithExistingMembers), xsd, new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + } + }); + + Assert.Contains( + @"public string[] Text_1 { get; set; }", + generatedType.First()); + } + + [Fact] + public void MixedTypeMustNotCollideWithContainingTypeName() + { + const string xsd = @" @@ -461,30 +462,30 @@ public void MixedTypeMustNotCollideWithContainingTypeName() "; - var generatedType = ConvertXml(nameof(MixedTypeMustNotCollideWithExistingMembers), xsd, new Generator - { - NamespaceProvider = new NamespaceProvider - { - GenerateNamespace = key => "Test" - } - }); - - Assert.Contains( - @"public string[] Text_1 { get; set; }", - generatedType.First()); - } - - [Theory] - [InlineData(@"xml/sameattributenames.xsd", @"xml/sameattributenames_import.xsd")] - public void CollidingAttributeAndPropertyNamesCanBeResolved(params string[] files) - { - // Compilation would previously throw due to duplicate type name within type - var assembly = Compiler.GenerateFiles("AttributesWithSameName", files); - - Assert.NotNull(assembly); - } - - [Fact] + var generatedType = ConvertXml(nameof(MixedTypeMustNotCollideWithExistingMembers), xsd, new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + } + }); + + Assert.Contains( + @"public string[] Text_1 { get; set; }", + generatedType.First()); + } + + [Theory] + [InlineData(@"xml/sameattributenames.xsd", @"xml/sameattributenames_import.xsd")] + public void CollidingAttributeAndPropertyNamesCanBeResolved(params string[] files) + { + // Compilation would previously throw due to duplicate type name within type + var assembly = Compiler.GenerateFiles("AttributesWithSameName", files); + + Assert.NotNull(assembly); + } + + [Fact] public void ComplexTypeWithAttributeGroupExtension() { const string xsd = @" @@ -630,6 +631,50 @@ public void ChoiceMembersAreNullable() Assert.Contains("Opt4Specified", content); } + [Fact] + public void AssemblyVisibleIsInternal() + { + // We test to see whether choices which are part of a larger ComplexType are marked as nullable. + // Because nullability isn't directly exposed in the generated C#, we use "XXXSpecified" on a value type + // as a proxy. + + const string xsd = @" + + + + + + + + + + + + + + + + + + + +"; + + var generator = new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + }, + AssemblyVisible = true + }; + var contents = ConvertXml(nameof(ComplexTypeWithAttributeGroupExtension), xsd, generator); + var content = Assert.Single(contents); + + Assert.Contains("internal partial class RootSub", content); + Assert.Contains("internal partial class Root", content); + } + private static void CompareOutput(string expected, string actual) { string Normalize(string input) => Regex.Replace(input, @"[ \t]*\r\n", "\n"); diff --git a/XmlSchemaClassGenerator/Generator.cs b/XmlSchemaClassGenerator/Generator.cs index b0f34d8c..9d1ed80b 100644 --- a/XmlSchemaClassGenerator/Generator.cs +++ b/XmlSchemaClassGenerator/Generator.cs @@ -75,6 +75,12 @@ public NamingScheme NamingScheme set { _configuration.NamingScheme = value; } } + public bool AssemblyVisible + { + get { return _configuration.AssemblyVisible; } + set { _configuration.AssemblyVisible = value; } + } + /// /// Emit the "Order" attribute value for XmlElementAttribute to ensure the correct order /// of the serialized XML elements. diff --git a/XmlSchemaClassGenerator/GeneratorConfiguration.cs b/XmlSchemaClassGenerator/GeneratorConfiguration.cs index 343f7db4..497fd4f4 100644 --- a/XmlSchemaClassGenerator/GeneratorConfiguration.cs +++ b/XmlSchemaClassGenerator/GeneratorConfiguration.cs @@ -129,7 +129,10 @@ public GeneratorConfiguration() /// Generate from XML comments. /// public bool GenerateDescriptionAttribute { get; set; } - + /// + /// Generate types as internal if true. public otherwise. + /// + public bool AssemblyVisible { get; set; } /// /// Generator Code reference options /// @@ -172,7 +175,7 @@ public void WriteLog(string message) public bool DisableComments { get; set; } public bool DoNotUseUnderscoreInPrivateMemberNames { get; set; } - + /// /// Check for Unique Particle Attribution (UPA) violations /// diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs index 225e4501..7fbe02c5 100644 --- a/XmlSchemaClassGenerator/TypeModel.cs +++ b/XmlSchemaClassGenerator/TypeModel.cs @@ -272,6 +272,11 @@ public override CodeTypeDeclaration Generate() classDeclaration.IsClass = true; classDeclaration.IsPartial = true; + if (Configuration.AssemblyVisible) + { + classDeclaration.TypeAttributes = (classDeclaration.TypeAttributes & ~System.Reflection.TypeAttributes.VisibilityMask) | System.Reflection.TypeAttributes.NestedAssembly; + } + if (Configuration.EnableDataBinding) { @@ -382,29 +387,29 @@ public override CodeTypeDeclaration Generate() keyProperty.IsKey = true; } - foreach (var property in Properties.GroupBy(x => x.Name)) - { - var propertyIndex = 0; - foreach (var p in property) - { - if (propertyIndex > 0) - { - p.Name += $"_{propertyIndex}"; - } - p.AddMembersTo(classDeclaration, Configuration.EnableDataBinding); - propertyIndex++; - } - } - - if (IsMixed && (BaseClass == null || (BaseClass is ClassModel && !AllBaseClasses.Any(b => b.IsMixed)))) - { - var propName = "Text"; - - // To not collide with any existing members - for (var propertyIndex = 1; Properties.Any(x => x.Name.Equals(propName, StringComparison.Ordinal)) || propName.Equals(classDeclaration.Name, StringComparison.Ordinal); propertyIndex++) - { - propName = $"Text_{propertyIndex}"; - } + foreach (var property in Properties.GroupBy(x => x.Name)) + { + var propertyIndex = 0; + foreach (var p in property) + { + if (propertyIndex > 0) + { + p.Name += $"_{propertyIndex}"; + } + p.AddMembersTo(classDeclaration, Configuration.EnableDataBinding); + propertyIndex++; + } + } + + if (IsMixed && (BaseClass == null || (BaseClass is ClassModel && !AllBaseClasses.Any(b => b.IsMixed)))) + { + var propName = "Text"; + + // To not collide with any existing members + for (var propertyIndex = 1; Properties.Any(x => x.Name.Equals(propName, StringComparison.Ordinal)) || propName.Equals(classDeclaration.Name, StringComparison.Ordinal); propertyIndex++) + { + propName = $"Text_{propertyIndex}"; + } var text = new CodeMemberField(typeof(string[]), propName); // hack to generate automatic property text.Name += " { get; set; }"; @@ -893,7 +898,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi var editorBrowsableAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(EditorBrowsableAttribute), Configuration.CodeTypeReferenceOptions)); editorBrowsableAttribute.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(new CodeTypeReference(typeof(EditorBrowsableState), Configuration.CodeTypeReferenceOptions)), "Never"))); - specifiedMember.CustomAttributes.Add(editorBrowsableAttribute); + specifiedMember.CustomAttributes.Add(editorBrowsableAttribute); member.CustomAttributes.Add(editorBrowsableAttribute); if (Configuration.EntityFramework) { member.CustomAttributes.Add(notMappedAttribute); } }