diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs index 1aaabae4..cd87db16 100644 --- a/XmlSchemaClassGenerator/ModelBuilder.cs +++ b/XmlSchemaClassGenerator/ModelBuilder.cs @@ -23,6 +23,9 @@ internal class ModelBuilder private string BuildKey(XmlSchemaAnnotated annotated, XmlQualifiedName name) => $"{annotated.GetType()}:{annotated.SourceUri}:{annotated.LineNumber}:{annotated.LinePosition}:{name}"; + private void SetType(XmlSchemaAnnotated annotated, XmlQualifiedName name, TypeModel type) + => Types[BuildKey(annotated, name)] = type; + public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) { _configuration = configuration; @@ -39,8 +42,7 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) UseDataTypeAttribute = false }; - var key = BuildKey(new XmlSchemaComplexType(), AnyType); - Types[key] = objectModel; + SetType(new XmlSchemaComplexType(), AnyType, objectModel); AttributeGroups = set.Schemas().Cast().SelectMany(s => s.AttributeGroups.Values.Cast()) .DistinctBy(g => g.QualifiedName.ToString()) @@ -52,16 +54,15 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) var dependencyOrder = new List(); var seenSchemas = new HashSet(); foreach (var schema in set.Schemas().Cast()) - { ResolveDependencies(schema, dependencyOrder, seenSchemas); - } foreach (var schema in dependencyOrder) { - var types = set.GlobalTypes.Values.Cast().Where(s => s.GetSchema() == schema); - CreateTypes(types); - var elements = set.GlobalElements.Values.Cast().Where(s => s.GetSchema() == schema); - CreateElements(elements); + foreach (var globalType in set.GlobalTypes.Values.Cast().Where(s => s.GetSchema() == schema)) + CreateTypeModel(globalType.QualifiedName, globalType); + + foreach (var rootElement in set.GlobalElements.Values.Cast().Where(s => s.GetSchema() == schema)) + CreateElement(rootElement); } CreateSubstitutes(); @@ -147,9 +148,7 @@ private void AddXmlRootAttributeToAmbiguousTypes() continue; } foreach (var typeModel in types) - { typeModel.RootElementName = typeModel.GetQualifiedName(); - } } } @@ -164,9 +163,7 @@ private void RemoveDuplicateInterfaceProperties() { var baseProperties = baseInterfaceTypeProperties.ToList(); foreach (var baseProperty in baseProperties.Where(baseProperty => parentProperty.Name == baseProperty.Name && parentProperty.Type.Name == baseProperty.Type.Name)) - { baseInterfaceTypeProperties.Remove(baseProperty); - } } } } @@ -208,9 +205,7 @@ private void PromoteInterfacePropertiesToCollection() if (derivedProperties.Any(p => p.IsCollection)) { foreach (var derivedProperty in derivedProperties.Where(p => !p.IsCollection)) - { derivedProperty.IsCollection = true; - } interfaceProperty.IsCollection = true; } @@ -224,9 +219,7 @@ private static void RenameInterfacePropertyInBaseClasses(InterfaceModel interfac foreach (var derivedClass in interfaceModel.AllDerivedReferenceTypes().Where(c => c != implementationClass)) { foreach (var propertyModel in derivedClass.Properties.Where(p => p.Name == interfaceProperty.Name)) - { propertyModel.Name = newName; - } } } @@ -251,125 +244,113 @@ private void ResolveDependencies(XmlSchema schema, List dependencyOrd dependencyOrder.Add(schema); } - private void CreateTypes(IEnumerable types) + private void CreateElement(XmlSchemaElement rootElement) { - foreach (var globalType in types) + var qualifiedName = rootElement.ElementSchemaType.QualifiedName; + if (qualifiedName.IsEmpty) + qualifiedName = rootElement.QualifiedName; + var type = CreateTypeModel(qualifiedName, rootElement.ElementSchemaType); + ClassModel derivedClassModel = null; + + if (type.RootElementName != null || type.IsAbstractRoot) { - CreateTypeModel(globalType.QualifiedName, globalType); + if (type is ClassModel classModel) + derivedClassModel = CreateDerivedRootClass(rootElement, type, classModel); + else + SetType(rootElement, rootElement.QualifiedName, type); } - } - - private void CreateElements(IEnumerable elements) - { - foreach (var rootElement in elements) + else { - var qualifiedName = rootElement.ElementSchemaType.QualifiedName; - if (qualifiedName.IsEmpty) { qualifiedName = rootElement.QualifiedName; } - var type = CreateTypeModel(qualifiedName, rootElement.ElementSchemaType); - ClassModel derivedClassModel = null; + if (type is ClassModel classModel) + classModel.Documentation.AddRange(GetDocumentation(rootElement)); - if (type.RootElementName != null || type.IsAbstractRoot) + type.RootElement = rootElement; + type.RootElementName = rootElement.QualifiedName; + } + + if (!rootElement.SubstitutionGroup.IsEmpty) + { + if (!SubstitutionGroups.TryGetValue(rootElement.SubstitutionGroup, out var substitutes)) { - if (type is ClassModel classModel) - { - // There is already another global element with this type. - // Need to create an empty derived class. + substitutes = new HashSet(); + SubstitutionGroups.Add(rootElement.SubstitutionGroup, substitutes); + } - var elementSource = CodeUtilities.CreateUri(rootElement.SourceUri); + substitutes.Add(new Substitute { Element = rootElement, Type = derivedClassModel ?? type }); + } + } - derivedClassModel = new ClassModel(_configuration) - { - Name = _configuration.NamingProvider.RootClassNameFromQualifiedName(rootElement.QualifiedName, rootElement), - Namespace = CreateNamespaceModel(elementSource, rootElement.QualifiedName) - }; + private ClassModel CreateDerivedRootClass(XmlSchemaElement rootElement, TypeModel type, ClassModel classModel) + { + ClassModel derivedClassModel; + // There is already another global element with this type. + // Need to create an empty derived class. - derivedClassModel.Documentation.AddRange(GetDocumentation(rootElement)); + var elementSource = CodeUtilities.CreateUri(rootElement.SourceUri); - if (derivedClassModel.Namespace != null) - { - derivedClassModel.Name = derivedClassModel.Namespace.GetUniqueTypeName(derivedClassModel.Name); - derivedClassModel.Namespace.Types[derivedClassModel.Name] = derivedClassModel; - } + derivedClassModel = new ClassModel(_configuration) + { + Name = _configuration.NamingProvider.RootClassNameFromQualifiedName(rootElement.QualifiedName, rootElement), + Namespace = CreateNamespaceModel(elementSource, rootElement.QualifiedName) + }; - var key = BuildKey(rootElement, rootElement.QualifiedName); - Types[key] = derivedClassModel; + derivedClassModel.Documentation.AddRange(GetDocumentation(rootElement)); - derivedClassModel.BaseClass = classModel; - ((ClassModel)derivedClassModel.BaseClass).DerivedTypes.Add(derivedClassModel); + if (derivedClassModel.Namespace != null) + { + derivedClassModel.Name = derivedClassModel.Namespace.GetUniqueTypeName(derivedClassModel.Name); + derivedClassModel.Namespace.Types[derivedClassModel.Name] = derivedClassModel; + } - derivedClassModel.RootElementName = rootElement.QualifiedName; + SetType(rootElement, rootElement.QualifiedName, derivedClassModel); - if (!type.IsAbstractRoot) - { - // Also create an empty derived class for the original root element + derivedClassModel.BaseClass = classModel; + ((ClassModel)derivedClassModel.BaseClass).DerivedTypes.Add(derivedClassModel); - var originalClassModel = new ClassModel(_configuration) - { - Name = _configuration.NamingProvider.RootClassNameFromQualifiedName(type.RootElementName, rootElement), - Namespace = classModel.Namespace - }; + derivedClassModel.RootElementName = rootElement.QualifiedName; - originalClassModel.Documentation.AddRange(classModel.Documentation); - classModel.Documentation.Clear(); + if (!type.IsAbstractRoot) + CreateOriginalRootClass(rootElement, type, classModel); - if (originalClassModel.Namespace != null) - { - originalClassModel.Name = originalClassModel.Namespace.GetUniqueTypeName(originalClassModel.Name); - originalClassModel.Namespace.Types[originalClassModel.Name] = originalClassModel; - } + return derivedClassModel; + } - if (classModel.XmlSchemaName?.IsEmpty == false) - { - key = BuildKey(classModel.RootElement, classModel.XmlSchemaName); - Types[key] = originalClassModel; - } + private void CreateOriginalRootClass(XmlSchemaElement rootElement, TypeModel type, ClassModel classModel) + { + // Also create an empty derived class for the original root element - originalClassModel.BaseClass = classModel; - ((ClassModel)originalClassModel.BaseClass).DerivedTypes.Add(originalClassModel); + var originalClassModel = new ClassModel(_configuration) + { + Name = _configuration.NamingProvider.RootClassNameFromQualifiedName(type.RootElementName, rootElement), + Namespace = classModel.Namespace + }; - originalClassModel.RootElementName = type.RootElementName; + originalClassModel.Documentation.AddRange(classModel.Documentation); + classModel.Documentation.Clear(); - if (classModel.RootElement.SubstitutionGroup != null - && SubstitutionGroups.TryGetValue(classModel.RootElement.SubstitutionGroup, out var substitutes)) - { - foreach (var substitute in substitutes.Where(s => s.Element == classModel.RootElement)) - { - substitute.Type = originalClassModel; - } - } + if (originalClassModel.Namespace != null) + { + originalClassModel.Name = originalClassModel.Namespace.GetUniqueTypeName(originalClassModel.Name); + originalClassModel.Namespace.Types[originalClassModel.Name] = originalClassModel; + } - classModel.RootElementName = null; - classModel.IsAbstractRoot = true; - } - } - else - { - var key = BuildKey(rootElement, rootElement.QualifiedName); - Types[key] = type; - } - } - else - { - if (type is ClassModel classModel) - { - classModel.Documentation.AddRange(GetDocumentation(rootElement)); - } + if (classModel.XmlSchemaName?.IsEmpty == false) + SetType(classModel.RootElement, classModel.XmlSchemaName, originalClassModel); - type.RootElement = rootElement; - type.RootElementName = rootElement.QualifiedName; - } + originalClassModel.BaseClass = classModel; + ((ClassModel)originalClassModel.BaseClass).DerivedTypes.Add(originalClassModel); - if (!rootElement.SubstitutionGroup.IsEmpty) - { - if (!SubstitutionGroups.TryGetValue(rootElement.SubstitutionGroup, out var substitutes)) - { - substitutes = new HashSet(); - SubstitutionGroups.Add(rootElement.SubstitutionGroup, substitutes); - } + originalClassModel.RootElementName = type.RootElementName; - substitutes.Add(new Substitute { Element = rootElement, Type = derivedClassModel ?? type }); - } + if (classModel.RootElement.SubstitutionGroup != null + && SubstitutionGroups.TryGetValue(classModel.RootElement.SubstitutionGroup, out var substitutes)) + { + foreach (var substitute in substitutes.Where(s => s.Element == classModel.RootElement)) + substitute.Type = originalClassModel; } + + classModel.RootElementName = null; + classModel.IsAbstractRoot = true; } private IEnumerable GetSubstitutedElements(XmlQualifiedName name) @@ -394,359 +375,410 @@ private TypeModel CreateTypeModel(XmlQualifiedName qualifiedName, XmlSchemaAnnot var namespaceModel = CreateNamespaceModel(source, qualifiedName); var docs = GetDocumentation(type); - return type switch - { - XmlSchemaGroup group => CreateTypeModel(qualifiedName, namespaceModel, docs, source, group), - XmlSchemaAttributeGroup attributeGroup => CreateTypeModel(qualifiedName, namespaceModel, docs, source, attributeGroup), - XmlSchemaComplexType complexType => CreateTypeModel(qualifiedName, namespaceModel, docs, source, complexType), - XmlSchemaSimpleType simpleType => CreateTypeModel(qualifiedName, namespaceModel, docs, simpleType), - _ => throw new NotSupportedException($"Cannot build declaration for {qualifiedName}"), - }; + var typeModelBuilder = new TypeModelBuilder(this, _configuration, qualifiedName, namespaceModel, docs, source); + + return typeModelBuilder.Create(type); } - private TypeModel CreateTypeModel(XmlQualifiedName qualifiedName, NamespaceModel namespaceModel, List docs, Uri source, XmlSchemaGroup group) + private class TypeModelBuilder { - var name = "I" + _configuration.NamingProvider.GroupTypeNameFromQualifiedName(qualifiedName, group); - if (namespaceModel != null) { name = namespaceModel.GetUniqueTypeName(name); } + private readonly ModelBuilder builder; + private readonly GeneratorConfiguration _configuration; + private readonly XmlQualifiedName qualifiedName; + private readonly NamespaceModel namespaceModel; + private readonly List docs; + private readonly Uri source; + + public TypeModelBuilder(ModelBuilder builder, GeneratorConfiguration configuration, XmlQualifiedName qualifiedName, NamespaceModel namespaceModel, List docs, Uri source) + { + this.builder = builder; + _configuration = configuration; + this.qualifiedName = qualifiedName; + this.namespaceModel = namespaceModel; + this.docs = docs; + this.source = source; + } - var interfaceModel = new InterfaceModel(_configuration) + internal TypeModel Create(XmlSchemaAnnotated type) => type switch { - Name = name, - Namespace = namespaceModel, - XmlSchemaName = qualifiedName + XmlSchemaGroup group => CreateTypeModel(group), + XmlSchemaAttributeGroup attributeGroup => CreateTypeModel(attributeGroup), + XmlSchemaComplexType complexType => CreateTypeModel(complexType), + XmlSchemaSimpleType simpleType => CreateTypeModel(simpleType), + _ => throw new NotSupportedException($"Cannot build declaration for {qualifiedName}"), }; - interfaceModel.Documentation.AddRange(docs); - - if (namespaceModel != null) { namespaceModel.Types[name] = interfaceModel; } - - if (!qualifiedName.IsEmpty) + private InterfaceModel CreateInterfaceModel(XmlSchemaAnnotated group, string name) { - var key = BuildKey(group, qualifiedName); - Types[key] = interfaceModel; - } + if (namespaceModel != null) + name = namespaceModel.GetUniqueTypeName(name); - var xmlParticle = group.Particle; - var particle = new Particle(xmlParticle, group.Parent); - var items = GetElements(xmlParticle); - var properties = CreatePropertiesForElements(source, interfaceModel, particle, items.Where(i => i.XmlParticle is not XmlSchemaGroupRef)); - interfaceModel.Properties.AddRange(properties); - var interfaces = items.Select(i => i.XmlParticle).OfType() - .Select(i => (InterfaceModel)CreateTypeModel(i.RefName, Groups[i.RefName])); - interfaceModel.AddInterfaces(interfaces); - - return interfaceModel; - } - - private TypeModel CreateTypeModel(XmlQualifiedName qualifiedName, NamespaceModel namespaceModel, List docs, Uri source, XmlSchemaAttributeGroup attributeGroup) - { - var name = "I" + _configuration.NamingProvider.AttributeGroupTypeNameFromQualifiedName(qualifiedName, attributeGroup); - if (namespaceModel != null) { name = namespaceModel.GetUniqueTypeName(name); } + var interfaceModel = new InterfaceModel(_configuration) + { + Name = name, + Namespace = namespaceModel, + XmlSchemaName = qualifiedName + }; - var interfaceModel = new InterfaceModel(_configuration) - { - Name = name, - Namespace = namespaceModel, - XmlSchemaName = qualifiedName - }; + interfaceModel.Documentation.AddRange(docs); - interfaceModel.Documentation.AddRange(docs); + if (namespaceModel != null) + namespaceModel.Types[name] = interfaceModel; - if (namespaceModel != null) { namespaceModel.Types[name] = interfaceModel; } + if (!qualifiedName.IsEmpty) + builder.SetType(group, qualifiedName, interfaceModel); + return interfaceModel; + } - if (!qualifiedName.IsEmpty) + private TypeModel CreateTypeModel(XmlSchemaGroup group) { - var key = BuildKey(attributeGroup, qualifiedName); - Types[key] = interfaceModel; - } + var name = "I" + _configuration.NamingProvider.GroupTypeNameFromQualifiedName(qualifiedName, group); - var items = attributeGroup.Attributes; - var properties = CreatePropertiesForAttributes(source, interfaceModel, items.OfType()); - interfaceModel.Properties.AddRange(properties); - var interfaces = items.OfType() - .Select(a => (InterfaceModel)CreateTypeModel(a.RefName, AttributeGroups[a.RefName])); - interfaceModel.AddInterfaces(interfaces); + InterfaceModel interfaceModel = CreateInterfaceModel(group, name); - return interfaceModel; - } + var xmlParticle = group.Particle; + var particle = new Particle(xmlParticle, group.Parent); + var items = builder.GetElements(xmlParticle); + var properties = builder.CreatePropertiesForElements(source, interfaceModel, particle, items.Where(i => i.XmlParticle is not XmlSchemaGroupRef)); + interfaceModel.Properties.AddRange(properties); + AddInterfaces(interfaceModel, items); - private TypeModel CreateTypeModel(XmlQualifiedName qualifiedName, NamespaceModel namespaceModel, List docs, Uri source, XmlSchemaComplexType complexType) - { - var name = _configuration.NamingProvider.ComplexTypeNameFromQualifiedName(qualifiedName, complexType); - if (namespaceModel != null) - { - name = namespaceModel.GetUniqueTypeName(name); + return interfaceModel; } - var classModel = new ClassModel(_configuration) + private TypeModel CreateTypeModel(XmlSchemaAttributeGroup group) { - Name = name, - Namespace = namespaceModel, - XmlSchemaName = qualifiedName, - XmlSchemaType = complexType, - IsAbstract = complexType.IsAbstract, - IsAnonymous = string.IsNullOrEmpty(complexType.QualifiedName.Name), - IsMixed = complexType.IsMixed, - IsSubstitution = complexType.Parent is XmlSchemaElement parent && !parent.SubstitutionGroup.IsEmpty - }; + var name = "I" + _configuration.NamingProvider.AttributeGroupTypeNameFromQualifiedName(qualifiedName, group); - classModel.Documentation.AddRange(docs); + InterfaceModel interfaceModel = CreateInterfaceModel(group, name); - if (namespaceModel != null) - { - namespaceModel.Types[classModel.Name] = classModel; - } + var attributes = group.Attributes; + var properties = builder.CreatePropertiesForAttributes(source, interfaceModel, attributes.OfType()); + interfaceModel.Properties.AddRange(properties); + AddInterfaces(interfaceModel, attributes); - if (!qualifiedName.IsEmpty) - { - var key = BuildKey(complexType, qualifiedName); - Types[key] = classModel; + return interfaceModel; } - if (complexType.BaseXmlSchemaType != null && complexType.BaseXmlSchemaType.QualifiedName != AnyType) + private TypeModel CreateTypeModel(XmlSchemaComplexType complexType) { - var baseModel = CreateTypeModel(complexType.BaseXmlSchemaType.QualifiedName, complexType.BaseXmlSchemaType); - classModel.BaseClass = baseModel; - if (baseModel is ClassModel baseClassModel) { baseClassModel.DerivedTypes.Add(classModel); } - } + var name = _configuration.NamingProvider.ComplexTypeNameFromQualifiedName(qualifiedName, complexType); + if (namespaceModel != null) + name = namespaceModel.GetUniqueTypeName(name); - XmlSchemaParticle xmlParticle = null; - if (classModel.BaseClass != null) - { - if (complexType.ContentModel.Content is XmlSchemaComplexContentExtension complexContent) + var classModel = new ClassModel(_configuration) { - xmlParticle = complexContent.Particle; - } - - // If it's a restriction, do not duplicate elements on the derived class, they're already in the base class. - // See https://msdn.microsoft.com/en-us/library/f3z3wh0y.aspx - } - else - { - xmlParticle = complexType.Particle ?? complexType.ContentTypeParticle; - } + Name = name, + Namespace = namespaceModel, + XmlSchemaName = qualifiedName, + XmlSchemaType = complexType, + IsAbstract = complexType.IsAbstract, + IsAnonymous = string.IsNullOrEmpty(complexType.QualifiedName.Name), + IsMixed = complexType.IsMixed, + IsSubstitution = complexType.Parent is XmlSchemaElement parent && !parent.SubstitutionGroup.IsEmpty + }; - var items = GetElements(xmlParticle, complexType).ToList(); + classModel.Documentation.AddRange(docs); - if (_configuration.GenerateInterfaces) - { - var interfaces = items.Select(i => i.XmlParticle).OfType() - .Select(i => (InterfaceModel)CreateTypeModel(i.RefName, Groups[i.RefName])).ToList(); + if (namespaceModel != null) + namespaceModel.Types[classModel.Name] = classModel; - classModel.AddInterfaces(interfaces); - } + if (!qualifiedName.IsEmpty) + builder.SetType(complexType, qualifiedName, classModel); - var particle = new Particle(xmlParticle, xmlParticle?.Parent); - var properties = CreatePropertiesForElements(source, classModel, particle, items); - classModel.Properties.AddRange(properties); + if (complexType.BaseXmlSchemaType != null && complexType.BaseXmlSchemaType.QualifiedName != AnyType) + { + var baseModel = builder.CreateTypeModel(complexType.BaseXmlSchemaType.QualifiedName, complexType.BaseXmlSchemaType); + classModel.BaseClass = baseModel; + if (baseModel is ClassModel baseClassModel) + baseClassModel.DerivedTypes.Add(classModel); + } - XmlSchemaObjectCollection attributes = null; - if (classModel.BaseClass != null) - { - if (complexType.ContentModel.Content is XmlSchemaComplexContentExtension complexContent) + XmlSchemaParticle xmlParticle = null; + if (classModel.BaseClass != null) { - attributes = complexContent.Attributes; + if (complexType.ContentModel.Content is XmlSchemaComplexContentExtension complexContent) + xmlParticle = complexContent.Particle; + + // If it's a restriction, do not duplicate elements on the derived class, they're already in the base class. + // See https://msdn.microsoft.com/en-us/library/f3z3wh0y.aspx } - else if (complexType.ContentModel.Content is XmlSchemaSimpleContentExtension simpleContent) + else { - attributes = simpleContent.Attributes; + xmlParticle = complexType.Particle ?? complexType.ContentTypeParticle; } - // If it's a restriction, do not duplicate attributes on the derived class, they're already in the base class. - // See https://msdn.microsoft.com/en-us/library/f3z3wh0y.aspx - } - else - { - attributes = complexType.Attributes; + var items = builder.GetElements(xmlParticle, complexType).ToList(); - if (attributes.Count == 0 && complexType.ContentModel != null) + if (_configuration.GenerateInterfaces) + AddInterfaces(classModel, items); + + var particle = new Particle(xmlParticle, xmlParticle?.Parent); + var properties = builder.CreatePropertiesForElements(source, classModel, particle, items); + classModel.Properties.AddRange(properties); + + XmlSchemaObjectCollection attributes = null; + if (classModel.BaseClass != null) { - var content = complexType.ContentModel.Content; + if (complexType.ContentModel.Content is XmlSchemaComplexContentExtension complexContent) + attributes = complexContent.Attributes; + else if (complexType.ContentModel.Content is XmlSchemaSimpleContentExtension simpleContent) + attributes = simpleContent.Attributes; - if (content is XmlSchemaComplexContentExtension extension) - attributes = extension.Attributes; - else if (content is XmlSchemaComplexContentRestriction restriction) - attributes = restriction.Attributes; + // If it's a restriction, do not duplicate attributes on the derived class, they're already in the base class. + // See https://msdn.microsoft.com/en-us/library/f3z3wh0y.aspx } - } + else + { + attributes = complexType.Attributes; - if (attributes != null) - { - var attributeProperties = CreatePropertiesForAttributes(source, classModel, attributes.Cast()); - classModel.Properties.AddRange(attributeProperties); + if (attributes.Count == 0 && complexType.ContentModel != null) + { + var content = complexType.ContentModel.Content; - if (_configuration.GenerateInterfaces) - { - var attributeInterfaces = attributes.OfType() - .Select(i => (InterfaceModel)CreateTypeModel(i.RefName, AttributeGroups[i.RefName])); - classModel.AddInterfaces(attributeInterfaces); + if (content is XmlSchemaComplexContentExtension extension) + attributes = extension.Attributes; + else if (content is XmlSchemaComplexContentRestriction restriction) + attributes = restriction.Attributes; + } } - } - XmlSchemaAnyAttribute anyAttribute = null; - if (complexType.AnyAttribute != null) - { - anyAttribute = complexType.AnyAttribute; - } - else if (complexType.AttributeWildcard != null) - { - var hasAnyAttribute = true; - for (var baseType = complexType.BaseXmlSchemaType; baseType != null; baseType = baseType.BaseXmlSchemaType) + if (attributes != null) { - if (baseType is not XmlSchemaComplexType baseComplexType) - continue; + var attributeProperties = builder.CreatePropertiesForAttributes(source, classModel, attributes.Cast()); + classModel.Properties.AddRange(attributeProperties); + + if (_configuration.GenerateInterfaces) + AddInterfaces(classModel, items); + } - if (baseComplexType.AttributeWildcard != null) + XmlSchemaAnyAttribute anyAttribute = null; + if (complexType.AnyAttribute != null) + { + anyAttribute = complexType.AnyAttribute; + } + else if (complexType.AttributeWildcard != null) + { + var hasAnyAttribute = true; + for (var baseType = complexType.BaseXmlSchemaType; baseType != null; baseType = baseType.BaseXmlSchemaType) { - hasAnyAttribute = false; - break; + if (baseType is not XmlSchemaComplexType baseComplexType) + continue; + + if (baseComplexType.AttributeWildcard != null) + { + hasAnyAttribute = false; + break; + } } + + if (hasAnyAttribute) + anyAttribute = complexType.AttributeWildcard; } - if (hasAnyAttribute) - anyAttribute = complexType.AttributeWildcard; + if (anyAttribute != null) + { + SimpleModel type = new(_configuration) { ValueType = typeof(XmlAttribute), UseDataTypeAttribute = false }; + var property = new PropertyModel(_configuration, "AnyAttribute", type, classModel) + { + IsAttribute = true, + IsCollection = true, + IsAny = true + }; + + var attributeDocs = GetDocumentation(anyAttribute); + property.Documentation.AddRange(attributeDocs); + + classModel.Properties.Add(property); + } + + return classModel; } - if (anyAttribute != null) + private TypeModel CreateTypeModel(XmlSchemaSimpleType simpleType) { - SimpleModel type = new(_configuration) { ValueType = typeof(XmlAttribute), UseDataTypeAttribute = false }; - var property = new PropertyModel(_configuration, "AnyAttribute", type, classModel) + List restrictions = null; + List> baseFacets = null; + + var facets = simpleType.Content switch { - IsAttribute = true, - IsCollection = true, - IsAny = true + XmlSchemaSimpleTypeRestriction typeRestriction => typeRestriction.Facets.Cast().ToList(), + XmlSchemaSimpleTypeUnion typeUnion when AllMembersHaveFacets(typeUnion, out baseFacets) => baseFacets.SelectMany(f => f).ToList(), + _ => new(), }; - var attributeDocs = GetDocumentation(anyAttribute); - property.Documentation.AddRange(attributeDocs); + if (facets.Count > 0) + { + var enumFacets = facets.OfType().ToList(); - classModel.Properties.Add(property); - } + // If a union has enum restrictions, there must be an enum restriction in all parts of the union + // If there are other restrictions mixed into the enumeration values, we'll generate a string to play it safe. + if (enumFacets.Count > 0 && (baseFacets is null || baseFacets.All(fs => fs.OfType().Any()))) + return CreateEnumModel(simpleType, enumFacets); - return classModel; - } + restrictions = GetRestrictions(facets, simpleType).Where(r => r != null).Sanitize().ToList(); + } - private TypeModel CreateTypeModel(XmlQualifiedName qualifiedName, NamespaceModel namespaceModel, List docs, XmlSchemaSimpleType simpleType) - { - var restrictions = new List(); - var allBasesHaveEnums = true; - List facets = new(); + return CreateSimpleModel(simpleType, restrictions ?? new()); - if (simpleType.Content is XmlSchemaSimpleTypeRestriction typeRestriction) - { - facets = typeRestriction.Facets.Cast().ToList(); - } - else if (simpleType.Content is XmlSchemaSimpleTypeUnion typeUnion - && typeUnion.BaseMemberTypes.All(b => b.Content is XmlSchemaSimpleTypeRestriction r && r.Facets.Count > 0)) - { - var baseFacets = typeUnion.BaseMemberTypes.Select(b => ((XmlSchemaSimpleTypeRestriction)b.Content).Facets.Cast()).ToList(); - // if a union has enum restrictions, there must be an enum restriction in all parts of the union - allBasesHaveEnums = baseFacets.All(fs => fs.OfType().Any()); - facets = baseFacets.SelectMany(f => f).ToList(); + static bool AllMembersHaveFacets(XmlSchemaSimpleTypeUnion typeUnion, out List> baseFacets) + { + var members = typeUnion.BaseMemberTypes.Select(b => b.Content as XmlSchemaSimpleTypeRestriction); + var retval = members.All(r => r?.Facets.Count > 0); + baseFacets = !retval ? null : members.Select(r => r.Facets.Cast()).ToList(); + return retval; + } } - if (facets.Count > 0) + private IEnumerable GetRestrictions(IEnumerable facets, XmlSchemaSimpleType type) { - var enumFacets = facets.OfType().ToList(); - // If there are other restrictions mixed into the enumeration values, we'll generate a string to play it safe. - var isEnum = enumFacets.Count > 0 && allBasesHaveEnums; + var min = facets.OfType().Select(f => int.Parse(f.Value)).DefaultIfEmpty().Max(); + var max = facets.OfType().Select(f => int.Parse(f.Value)).DefaultIfEmpty().Min(); - if (isEnum) + if (_configuration.DataAnnotationMode == DataAnnotationMode.All) { - // we got an enum - var name = _configuration.NamingProvider.EnumTypeNameFromQualifiedName(qualifiedName, simpleType); - if (namespaceModel != null) { name = namespaceModel.GetUniqueTypeName(name); } + if (min > 0) yield return new MinLengthRestrictionModel(_configuration) { Value = min }; + if (max > 0) yield return new MaxLengthRestrictionModel(_configuration) { Value = max }; + } + else if (min > 0 || max > 0) + { + yield return new MinMaxLengthRestrictionModel(_configuration) { Min = min, Max = max }; + } - var enumModel = new EnumModel(_configuration) + foreach (var facet in facets) + { + var valueType = type.Datatype.ValueType; + switch (facet) { - Name = name, - Namespace = namespaceModel, - XmlSchemaName = qualifiedName, - XmlSchemaType = simpleType, - IsAnonymous = string.IsNullOrEmpty(simpleType.QualifiedName.Name), - }; + case XmlSchemaLengthFacet: + var value = int.Parse(facet.Value); + if (_configuration.DataAnnotationMode == DataAnnotationMode.All) + { + yield return new MinLengthRestrictionModel(_configuration) { Value = value }; + yield return new MaxLengthRestrictionModel(_configuration) { Value = value }; + } + else + { + yield return new MinMaxLengthRestrictionModel(_configuration) { Min = value, Max = value }; + } + break; + case XmlSchemaTotalDigitsFacet: + yield return new TotalDigitsRestrictionModel(_configuration) { Value = int.Parse(facet.Value) }; break; + case XmlSchemaFractionDigitsFacet: + yield return new FractionDigitsRestrictionModel(_configuration) { Value = int.Parse(facet.Value) }; break; + case XmlSchemaPatternFacet: + yield return new PatternRestrictionModel(_configuration) { Value = facet.Value }; break; + case XmlSchemaMinInclusiveFacet: + yield return new MinInclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; + case XmlSchemaMinExclusiveFacet: + yield return new MinExclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; + case XmlSchemaMaxInclusiveFacet: + yield return new MaxInclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; + case XmlSchemaMaxExclusiveFacet: + yield return new MaxExclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; + } + } + } - enumModel.Documentation.AddRange(docs); + private static List EnsureEnumValuesUnique(List enumModelValues) + { + var enumValueGroups = from enumValue in enumModelValues + group enumValue by enumValue.Name; - foreach (var facet in enumFacets.DistinctBy(f => f.Value)) - { - var value = new EnumValueModel - { - Name = _configuration.NamingProvider.EnumMemberNameFromValue(enumModel.Name, facet.Value, facet), - Value = facet.Value - }; + foreach (var g in enumValueGroups) + { + var i = 1; + foreach (var t in g.Skip(1)) + t.Name = $"{t.Name}{i++}"; + } - var valueDocs = GetDocumentation(facet); - value.Documentation.AddRange(valueDocs); + return enumModelValues; + } - value.IsDeprecated = facet.Annotation?.Items.OfType() - .Any(a => a.Markup.Any(m => m.Name == "annox:annotate" && m.HasChildNodes && m.FirstChild.Name == "jl:Deprecated")) == true; + private EnumModel CreateEnumModel(XmlSchemaSimpleType simpleType, List enumFacets) + { + // we got an enum + var name = _configuration.NamingProvider.EnumTypeNameFromQualifiedName(qualifiedName, simpleType); + if (namespaceModel != null) + name = namespaceModel.GetUniqueTypeName(name); - enumModel.Values.Add(value); - } + var enumModel = new EnumModel(_configuration) + { + Name = name, + Namespace = namespaceModel, + XmlSchemaName = qualifiedName, + XmlSchemaType = simpleType, + IsAnonymous = string.IsNullOrEmpty(simpleType.QualifiedName.Name), + }; - enumModel.Values = EnsureEnumValuesUnique(enumModel.Values); - if (namespaceModel != null) - { - namespaceModel.Types[enumModel.Name] = enumModel; - } + enumModel.Documentation.AddRange(docs); - if (!qualifiedName.IsEmpty) + foreach (var facet in enumFacets.DistinctBy(f => f.Value)) + { + var value = new EnumValueModel { - var key = BuildKey(simpleType, qualifiedName); - Types[key] = enumModel; - } + Name = _configuration.NamingProvider.EnumMemberNameFromValue(enumModel.Name, facet.Value, facet), + Value = facet.Value + }; - return enumModel; - } + var valueDocs = GetDocumentation(facet); + value.Documentation.AddRange(valueDocs); - restrictions = GetRestrictions(facets, simpleType).Where(r => r != null).Sanitize().ToList(); - } + value.IsDeprecated = facet.Annotation?.Items.OfType() + .Any(a => a.Markup.Any(m => m.Name == "annox:annotate" && m.HasChildNodes && m.FirstChild.Name == "jl:Deprecated")) == true; - var simpleModelName = _configuration.NamingProvider.SimpleTypeNameFromQualifiedName(qualifiedName, simpleType); - if (namespaceModel != null) { simpleModelName = namespaceModel.GetUniqueTypeName(simpleModelName); } + enumModel.Values.Add(value); + } - var simpleModel = new SimpleModel(_configuration) - { - Name = simpleModelName, - Namespace = namespaceModel, - XmlSchemaName = qualifiedName, - XmlSchemaType = simpleType, - ValueType = simpleType.Datatype.GetEffectiveType(_configuration, restrictions), - }; + enumModel.Values = EnsureEnumValuesUnique(enumModel.Values); + if (namespaceModel != null) + namespaceModel.Types[enumModel.Name] = enumModel; - simpleModel.Documentation.AddRange(docs); - simpleModel.Restrictions.AddRange(restrictions); + if (!qualifiedName.IsEmpty) + builder.SetType(simpleType, qualifiedName, enumModel); - if (namespaceModel != null) - { - namespaceModel.Types[simpleModel.Name] = simpleModel; + return enumModel; } - if (!qualifiedName.IsEmpty) + private SimpleModel CreateSimpleModel(XmlSchemaSimpleType simpleType, List restrictions) { - var key = BuildKey(simpleType, qualifiedName); - Types[key] = simpleModel; - } + var simpleModelName = _configuration.NamingProvider.SimpleTypeNameFromQualifiedName(qualifiedName, simpleType); + if (namespaceModel != null) + simpleModelName = namespaceModel.GetUniqueTypeName(simpleModelName); - return simpleModel; - } + var simpleModel = new SimpleModel(_configuration) + { + Name = simpleModelName, + Namespace = namespaceModel, + XmlSchemaName = qualifiedName, + XmlSchemaType = simpleType, + ValueType = simpleType.Datatype.GetEffectiveType(_configuration, restrictions), + }; - private static List EnsureEnumValuesUnique(List enumModelValues) - { - var enumValueGroups = from enumValue in enumModelValues - group enumValue by enumValue.Name; + simpleModel.Documentation.AddRange(docs); + simpleModel.Restrictions.AddRange(restrictions); + + if (namespaceModel != null) + namespaceModel.Types[simpleModel.Name] = simpleModel; + + if (!qualifiedName.IsEmpty) + builder.SetType(simpleType, qualifiedName, simpleModel); + return simpleModel; + } - foreach (var g in enumValueGroups) + private void AddInterfaces(ReferenceTypeModel refTypeModel, IEnumerable items) { - var i = 1; - foreach (var t in g.Skip(1)) - { - t.Name = $"{t.Name}{i++}"; - } + var interfaces = items.Select(i => i.XmlParticle).OfType() + .Select(i => (InterfaceModel)builder.CreateTypeModel(i.RefName, builder.Groups[i.RefName])); + refTypeModel.AddInterfaces(interfaces); } - return enumModelValues; + private void AddInterfaces(ReferenceTypeModel refTypeModel, XmlSchemaObjectCollection attributes) + { + var interfaces = attributes.OfType() + .Select(a => (InterfaceModel)builder.CreateTypeModel(a.RefName, builder.AttributeGroups[a.RefName])); + refTypeModel.AddInterfaces(interfaces); + } } private IEnumerable CreatePropertiesForAttributes(Uri source, TypeModel owningTypeModel, IEnumerable items) @@ -755,163 +787,129 @@ private IEnumerable CreatePropertiesForAttributes(Uri source, Typ foreach (var item in items) { - if (item is XmlSchemaAttribute xs) + switch (item) { - XmlSchemaAttributeEx attribute = xs; - if (attribute.Use != XmlSchemaUse.Prohibited) - { - var attributeQualifiedName = attribute.AttributeSchemaType.QualifiedName; - var name = _configuration.NamingProvider.AttributeNameFromQualifiedName(attribute.QualifiedName, attribute); - var originalName = name; - - if (attribute.Base.Parent is XmlSchemaAttributeGroup attributeGroup - && attributeGroup.QualifiedName != owningTypeModel.XmlSchemaName - && Types.TryGetValue(BuildKey(attributeGroup, attributeGroup.QualifiedName), out var typeModelValue) - && typeModelValue is InterfaceModel interfaceTypeModel) - { - var interfaceProperty = interfaceTypeModel.Properties.Single(p => p.XmlSchemaName == attribute.QualifiedName); - attributeQualifiedName = interfaceProperty.Type.XmlSchemaName; - name = interfaceProperty.Name; - } - else + case XmlSchemaAttribute attribute when attribute.Use != XmlSchemaUse.Prohibited: + properties.Add(PropertyFromAttribute(owningTypeModel, attribute)); + break; + case XmlSchemaAttributeGroupRef attributeGroupRef: + if (_configuration.GenerateInterfaces) + CreateTypeModel(attributeGroupRef.RefName, AttributeGroups[attributeGroupRef.RefName]); + + var attributeGroup = AttributeGroups[attributeGroupRef.RefName]; + var attributes = attributeGroup.Attributes.Cast() + .Where(a => !(a is XmlSchemaAttributeGroupRef agr && agr.RefName == attributeGroupRef.RefName)) + .ToList(); + + if (attributeGroup.RedefinedAttributeGroup != null) { - if (attributeQualifiedName.IsEmpty) + foreach (var attr in attributeGroup.RedefinedAttributeGroup.Attributes.Cast()) { - attributeQualifiedName = attribute.QualifiedName; - - if (attributeQualifiedName.IsEmpty || string.IsNullOrEmpty(attributeQualifiedName.Namespace)) - { - // inner type, have to generate a type name - var typeName = _configuration.NamingProvider.PropertyNameFromAttribute(owningTypeModel.Name, attribute.QualifiedName.Name, attribute); - attributeQualifiedName = new XmlQualifiedName(typeName, owningTypeModel.XmlSchemaName.Namespace); - // try to avoid name clashes - if (NameExists(attributeQualifiedName)) - { - attributeQualifiedName = new[] { "Item", "Property", "Element" } - .Select(s => new XmlQualifiedName(attributeQualifiedName.Name + s, attributeQualifiedName.Namespace)) - .First(n => !NameExists(n)); - } - } - } + var n = attr.GetQualifiedName(); - if (name == owningTypeModel.Name) - { - name += "Property"; // member names cannot be the same as their enclosing type + if (n != null) + attributes.RemoveAll(a => a.GetQualifiedName() == n); + + attributes.Add(attr); } } - name = owningTypeModel.GetUniquePropertyName(name); - - var typeModel = CreateTypeModel(attributeQualifiedName, attribute.AttributeSchemaType); - var property = new PropertyModel(_configuration, name, typeModel, owningTypeModel) - { - IsAttribute = true, - IsRequired = attribute.Use == XmlSchemaUse.Required - }; - - property.SetFromNode(originalName, () => attribute.Use != XmlSchemaUse.Optional, attribute); - property.SetSchemaNameAndNamespace(owningTypeModel, attribute); - property.Documentation.AddRange(GetDocumentation(attribute)); - - properties.Add(property); - } + properties.AddRange(CreatePropertiesForAttributes(source, owningTypeModel, attributes)); + break; } - else if (item is XmlSchemaAttributeGroupRef attributeGroupRef) + } + return properties; + } + + private PropertyModel PropertyFromAttribute(TypeModel owningTypeModel, XmlSchemaAttributeEx attribute) + { + var attributeQualifiedName = attribute.AttributeSchemaType.QualifiedName; + var name = _configuration.NamingProvider.AttributeNameFromQualifiedName(attribute.QualifiedName, attribute); + var originalName = name; + + if (attribute.Base.Parent is XmlSchemaAttributeGroup attributeGroup + && attributeGroup.QualifiedName != owningTypeModel.XmlSchemaName + && Types.TryGetValue(BuildKey(attributeGroup, attributeGroup.QualifiedName), out var typeModelValue) + && typeModelValue is InterfaceModel interfaceTypeModel) + { + var interfaceProperty = interfaceTypeModel.Properties.Single(p => p.XmlSchemaName == attribute.QualifiedName); + attributeQualifiedName = interfaceProperty.Type.XmlSchemaName; + name = interfaceProperty.Name; + } + else + { + if (attributeQualifiedName.IsEmpty) { - if (_configuration.GenerateInterfaces) + attributeQualifiedName = attribute.QualifiedName; + + if (attributeQualifiedName.IsEmpty || string.IsNullOrEmpty(attributeQualifiedName.Namespace)) { - CreateTypeModel(attributeGroupRef.RefName, AttributeGroups[attributeGroupRef.RefName]); + // inner type, have to generate a type name + var typeName = _configuration.NamingProvider.PropertyNameFromAttribute(owningTypeModel.Name, attribute.QualifiedName.Name, attribute); + attributeQualifiedName = new XmlQualifiedName(typeName, owningTypeModel.XmlSchemaName.Namespace); + // try to avoid name clashes + if (NameExists(attributeQualifiedName)) + attributeQualifiedName = new[] { "Item", "Property", "Element" }.Select(s => new XmlQualifiedName(attributeQualifiedName.Name + s, attributeQualifiedName.Namespace)).First(n => !NameExists(n)); } + } - var attributeGroup = AttributeGroups[attributeGroupRef.RefName]; - var attributes = attributeGroup.Attributes.Cast() - .Where(a => !(a is XmlSchemaAttributeGroupRef agr && agr.RefName == attributeGroupRef.RefName)) - .ToList(); - - if (attributeGroup.RedefinedAttributeGroup != null) - { - foreach (var attr in attributeGroup.RedefinedAttributeGroup.Attributes.Cast()) - { - var n = attr.GetQualifiedName(); + if (name == owningTypeModel.Name) + name += "Property"; + } - if (n != null) - { - attributes.RemoveAll(a => a.GetQualifiedName() == n); - } + name = owningTypeModel.GetUniquePropertyName(name); - attributes.Add(attr); - } - } + var typeModel = CreateTypeModel(attributeQualifiedName, attribute.AttributeSchemaType); + var property = new PropertyModel(_configuration, name, typeModel, owningTypeModel) + { + IsAttribute = true, + IsRequired = attribute.Use == XmlSchemaUse.Required + }; - var groupProperties = CreatePropertiesForAttributes(source, owningTypeModel, attributes); - properties.AddRange(groupProperties); - } - } + property.SetFromNode(originalName, () => attribute.Use != XmlSchemaUse.Optional, attribute); + property.SetSchemaNameAndNamespace(owningTypeModel, attribute); + property.Documentation.AddRange(GetDocumentation(attribute)); - return properties; + return property; } private IEnumerable CreatePropertiesForElements(Uri source, TypeModel owningTypeModel, Particle particle, IEnumerable items, Substitute substitute = null, int order = 0) { var properties = new List(); - var xmlParticle = particle.XmlParticle; foreach (var item in items) { PropertyModel property = null; - // ElementSchemaType must be non-null. This is not the case when maxOccurs="0". - if (item.XmlParticle is XmlSchemaElement xs && xs.ElementSchemaType != null) - { - XmlSchemaElementEx element = xs; - XmlSchemaElementEx effectiveElement = substitute?.Element ?? element; - var name = _configuration.NamingProvider.ElementNameFromQualifiedName(effectiveElement.QualifiedName, effectiveElement); - var originalName = name; - if (name == owningTypeModel.Name) - name += "Property"; // member names cannot be the same as their enclosing type - - name = owningTypeModel.GetUniquePropertyName(name); - - var typeModel = substitute?.Type ?? CreateTypeModel(GetQualifiedName(owningTypeModel, xmlParticle, element), element.ElementSchemaType); - - property = new PropertyModel(_configuration, name, typeModel, owningTypeModel) { IsNillable = element.IsNillable }; - property.SetFromParticles(particle, item); - property.SetFromNode(originalName, () => item.MinOccurs >= 1.0m && item.XmlParent is not XmlSchemaChoice, element); - property.SetSchemaNameAndNamespace(owningTypeModel, effectiveElement); - - if (property.IsArray && !_configuration.GenerateComplexTypesForCollections) - { - property.Type.Namespace.Types.Remove(property.Type.Name); - } - } - else + switch (item.XmlParticle) { - switch (item.XmlParticle) - { - case XmlSchemaAny any: - SimpleModel typeModel = new(_configuration) - { - ValueType = _configuration.UseXElementForAny ? typeof(XElement) : typeof(XmlElement), - UseDataTypeAttribute = false - }; - property = new PropertyModel(_configuration, "Any", typeModel, owningTypeModel) { IsAny = true }; - property.SetFromParticles(particle, item); - break; - case XmlSchemaGroupRef groupRef: - var group = Groups[groupRef.RefName]; + // ElementSchemaType must be non-null. This is not the case when maxOccurs="0". + case XmlSchemaElement element when element.ElementSchemaType != null: + property = PropertyFromElement(owningTypeModel, element, particle, item, substitute); + break; + case XmlSchemaAny any: + SimpleModel typeModel = new(_configuration) + { + ValueType = _configuration.UseXElementForAny ? typeof(XElement) : typeof(XmlElement), + UseDataTypeAttribute = false + }; + property = new PropertyModel(_configuration, "Any", typeModel, owningTypeModel) { IsAny = true }; + property.SetFromParticles(particle, item); + break; + case XmlSchemaGroupRef groupRef: + var group = Groups[groupRef.RefName]; - if (_configuration.GenerateInterfaces) - CreateTypeModel(groupRef.RefName, group); + if (_configuration.GenerateInterfaces) + CreateTypeModel(groupRef.RefName, group); - var groupItems = GetElements(groupRef.Particle).ToList(); - var groupProperties = CreatePropertiesForElements(source, owningTypeModel, item, groupItems, order: order).ToList(); - if (_configuration.EmitOrder) - order += groupProperties.Count; + var groupItems = GetElements(groupRef.Particle).ToList(); + var groupProperties = CreatePropertiesForElements(source, owningTypeModel, item, groupItems, order: order).ToList(); + if (_configuration.EmitOrder) + order += groupProperties.Count; - properties.AddRange(groupProperties); - break; - } + properties.AddRange(groupProperties); + break; } // Discard duplicate property names. This is most likely due to: @@ -934,6 +932,30 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM return properties; } + private PropertyModel PropertyFromElement(TypeModel owningTypeModel, XmlSchemaElementEx element, Particle particle, Particle item, Substitute substitute) + { + PropertyModel property; + XmlSchemaElementEx effectiveElement = substitute?.Element ?? element; + var name = _configuration.NamingProvider.ElementNameFromQualifiedName(effectiveElement.QualifiedName, effectiveElement); + var originalName = name; + if (name == owningTypeModel.Name) + name += "Property"; // member names cannot be the same as their enclosing type + + name = owningTypeModel.GetUniquePropertyName(name); + + var typeModel = substitute?.Type ?? CreateTypeModel(GetQualifiedName(owningTypeModel, particle.XmlParticle, element), element.ElementSchemaType); + + property = new PropertyModel(_configuration, name, typeModel, owningTypeModel) { IsNillable = element.IsNillable }; + property.SetFromParticles(particle, item); + property.SetFromNode(originalName, () => item.MinOccurs >= 1.0m && item.XmlParent is not XmlSchemaChoice, element); + property.SetSchemaNameAndNamespace(owningTypeModel, effectiveElement); + + if (property.IsArray && !_configuration.GenerateComplexTypesForCollections) + property.Type.Namespace.Types.Remove(property.Type.Name); + + return property; + } + private XmlQualifiedName GetQualifiedName(TypeModel typeModel, XmlSchemaParticle xmlParticle, XmlSchemaElementEx element) { var elementQualifiedName = element.ElementSchemaType.QualifiedName; @@ -950,11 +972,7 @@ private XmlQualifiedName GetQualifiedName(TypeModel typeModel, XmlSchemaParticle elementQualifiedName = new XmlQualifiedName(typeName, typeModel.XmlSchemaName.Namespace); // try to avoid name clashes if (NameExists(elementQualifiedName)) - { - elementQualifiedName = new[] { "Item", "Property", "Element" } - .Select(s => new XmlQualifiedName(elementQualifiedName.Name + s, elementQualifiedName.Namespace)) - .First(n => !NameExists(n)); - } + elementQualifiedName = new[] { "Item", "Property", "Element" }.Select(s => new XmlQualifiedName(elementQualifiedName.Name + s, elementQualifiedName.Namespace)).First(n => !NameExists(n)); } } @@ -984,56 +1002,6 @@ private bool NameExists(XmlQualifiedName name) return elements.Concat(types).Any(n => n.Namespace == name.Namespace && name.Name.Equals(n.Name, StringComparison.OrdinalIgnoreCase)); } - private IEnumerable GetRestrictions(IEnumerable facets, XmlSchemaSimpleType type) - { - var min = facets.OfType().Select(f => int.Parse(f.Value)).DefaultIfEmpty().Max(); - var max = facets.OfType().Select(f => int.Parse(f.Value)).DefaultIfEmpty().Min(); - - if (_configuration.DataAnnotationMode == DataAnnotationMode.All) - { - if (min > 0) yield return new MinLengthRestrictionModel(_configuration) { Value = min }; - if (max > 0) yield return new MaxLengthRestrictionModel(_configuration) { Value = max }; - } - else if (min > 0 || max > 0) - { - yield return new MinMaxLengthRestrictionModel(_configuration) { Min = min, Max = max }; - } - - foreach (var facet in facets) - { - var valueType = type.Datatype.ValueType; - switch (facet) - { - case XmlSchemaLengthFacet: - var value = int.Parse(facet.Value); - if (_configuration.DataAnnotationMode == DataAnnotationMode.All) - { - yield return new MinLengthRestrictionModel(_configuration) { Value = value }; - yield return new MaxLengthRestrictionModel(_configuration) { Value = value }; - } - else - { - yield return new MinMaxLengthRestrictionModel(_configuration) { Min = value, Max = value }; - } - break; - case XmlSchemaTotalDigitsFacet: - yield return new TotalDigitsRestrictionModel(_configuration) { Value = int.Parse(facet.Value) }; break; - case XmlSchemaFractionDigitsFacet: - yield return new FractionDigitsRestrictionModel(_configuration) { Value = int.Parse(facet.Value) }; break; - case XmlSchemaPatternFacet: - yield return new PatternRestrictionModel(_configuration) { Value = facet.Value }; break; - case XmlSchemaMinInclusiveFacet: - yield return new MinInclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; - case XmlSchemaMinExclusiveFacet: - yield return new MinExclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; - case XmlSchemaMaxInclusiveFacet: - yield return new MaxInclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; - case XmlSchemaMaxExclusiveFacet: - yield return new MaxExclusiveRestrictionModel(_configuration) { Value = facet.Value, Type = valueType }; break; - } - } - } - public IEnumerable GetElements(XmlSchemaGroupBase groupBase) { if (groupBase?.Items != null) diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs index 399bb944..13290093 100644 --- a/XmlSchemaClassGenerator/TypeModel.cs +++ b/XmlSchemaClassGenerator/TypeModel.cs @@ -149,14 +149,9 @@ public virtual CodeExpression GetDefaultValueFor(string defaultString, bool attr public class InterfaceModel : ReferenceTypeModel { - public InterfaceModel(GeneratorConfiguration configuration) - : base(configuration) - { - Properties = new List(); - DerivedTypes = new List(); - } + public InterfaceModel(GeneratorConfiguration configuration) : base(configuration) { } - public List DerivedTypes { get; set; } + public List DerivedTypes { get; } = new(); public override CodeTypeDeclaration Generate() { @@ -219,14 +214,10 @@ public class ClassModel : ReferenceTypeModel public bool IsMixed { get; set; } public bool IsSubstitution { get; set; } public TypeModel BaseClass { get; set; } - public List DerivedTypes { get; set; } + public List DerivedTypes { get; set; } = new(); public override bool IsSubtype => BaseClass != null; - public ClassModel(GeneratorConfiguration configuration) - : base(configuration) - { - DerivedTypes = new List(); - } + public ClassModel(GeneratorConfiguration configuration) : base(configuration) { } public IEnumerable AllBaseClasses { @@ -476,15 +467,10 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att public class ReferenceTypeModel : TypeModel { - public ReferenceTypeModel(GeneratorConfiguration configuration) - : base(configuration) - { - Properties = new List(); - Interfaces = new List(); - } + public ReferenceTypeModel(GeneratorConfiguration configuration) : base(configuration) { } - public List Properties { get; set; } - public List Interfaces { get; } + public List Properties { get; } = new(); + public List Interfaces { get; } = new(); public void AddInterfaces(IEnumerable interfaces) {