public override CodeTypeDeclaration Generate() { var classDeclaration = base.Generate(); GenerateSerializableAttribute(classDeclaration); GenerateTypeAttribute(classDeclaration); classDeclaration.IsClass = true; classDeclaration.IsPartial = true; if (EnableDataBinding) { classDeclaration.Members.Add(new CodeMemberEvent() { Name = "PropertyChanged", Type = new CodeTypeReference("PropertyChangedEventHandler"), Attributes = MemberAttributes.Public, }); var onPropChangedMethod = new CodeMemberMethod { Name = "OnPropertyChanged", Attributes = MemberAttributes.Family, }; var param = new CodeParameterDeclarationExpression(typeof(string), "propertyName"); onPropChangedMethod.Parameters.Add(param); onPropChangedMethod.Statements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeEventReferenceExpression(null, "PropertyChanged"), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)), new CodeExpressionStatement(new CodeDelegateInvokeExpression( new CodeEventReferenceExpression(null, "PropertyChanged"), new CodeThisReferenceExpression(), new CodeObjectCreateExpression( "PropertyChangedEventArgs", new CodeArgumentReferenceExpression("propertyName")) )))); classDeclaration.Members.Add(onPropChangedMethod); } if (BaseClass != null) { if (BaseClass is ClassModel) classDeclaration.BaseTypes.Add(BaseClass.GetReferenceFor(Namespace, false)); else { var typeReference = BaseClass.GetReferenceFor(Namespace, false); var member = new CodeMemberField(typeReference, "Value") { Attributes = MemberAttributes.Public, }; if (EnableDataBinding) { var backingFieldMember = new CodeMemberField(typeReference, member.Name.ToBackingField()) { Attributes = MemberAttributes.Private }; member.Name += PropertyModel.GetAccessors(member.Name, backingFieldMember.Name, BaseClass.GetPropertyValueTypeCode(), false); classDeclaration.Members.Add(backingFieldMember); } else { // hack to generate automatic property member.Name += " { get; set; }"; } var docs = new[] { new DocumentationModel { Language = "en", Text = "Gets or sets the text value." }, new DocumentationModel { Language = "de", Text = "Ruft den Text ab oder legt diesen fest." } }; member.Comments.AddRange(DocumentationModel.GetComments(docs).ToArray()); var attribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlTextAttribute))); var simpleModel = BaseClass as SimpleModel; if (simpleModel != null && (simpleModel.XmlSchemaType.IsDataTypeAttributeAllowed(Configuration) ?? simpleModel.UseDataTypeAttribute)) { var name = BaseClass.GetQualifiedName(); if (name.Namespace == XmlSchema.Namespace) { var dataType = new CodeAttributeArgument("DataType", new CodePrimitiveExpression(name.Name)); attribute.Arguments.Add(dataType); } } member.CustomAttributes.Add(attribute); classDeclaration.Members.Add(member); } } if (EnableDataBinding) classDeclaration.BaseTypes.Add(new CodeTypeReference("INotifyPropertyChanged")); if (Configuration.EntityFramework && (BaseClass == null || !(BaseClass is ClassModel))) { // generate key var keyProperty = Properties.FirstOrDefault(p => p.Name.ToLowerInvariant() == "id") ?? Properties.FirstOrDefault(p => p.Name.ToLowerInvariant() == (Name.ToLowerInvariant() + "id")); if (keyProperty == null) { keyProperty = new PropertyModel(Configuration) { 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." } } }; Properties.Insert(0, keyProperty); } keyProperty.IsKey = true; } foreach (var property in Properties) property.AddMembersTo(classDeclaration, EnableDataBinding); if (IsMixed && (BaseClass == null || BaseClass is ClassModel)) { var text = new CodeMemberField(typeof(string), "Text"); // hack to generate automatic property text.Name += " { get; set; }"; text.Attributes = MemberAttributes.Public; var xmlTextAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlTextAttribute))); text.CustomAttributes.Add(xmlTextAttribute); classDeclaration.Members.Add(text); } classDeclaration.CustomAttributes.Add( new CodeAttributeDeclaration(new CodeTypeReference(typeof(DebuggerStepThroughAttribute)))); if (Configuration.GenerateDesignerCategoryAttribute) { classDeclaration.CustomAttributes.Add( new CodeAttributeDeclaration(new CodeTypeReference(typeof (DesignerCategoryAttribute)), new CodeAttributeArgument(new CodePrimitiveExpression("code")))); } if (RootElementName != null) { var rootAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlRootAttribute)), new CodeAttributeArgument(new CodePrimitiveExpression(RootElementName.Name)), new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(RootElementName.Namespace))); classDeclaration.CustomAttributes.Add(rootAttribute); } var derivedTypes = GetAllDerivedTypes(); foreach (var derivedType in derivedTypes.OrderBy(t => t.Name)) { var includeAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlIncludeAttribute)), new CodeAttributeArgument(new CodeTypeOfExpression(derivedType.GetReferenceFor(Namespace, false)))); classDeclaration.CustomAttributes.Add(includeAttribute); } classDeclaration.BaseTypes.AddRange(Interfaces.Select(i => i.GetReferenceFor(Namespace, false)).ToArray()); return classDeclaration; }
public static string GetUniqueFieldName(this TypeModel typeModel, PropertyModel propertyModel) { var propBackingFieldName = propertyModel.Name.ToBackingField(); var classModel = typeModel as ClassModel; if (classModel == null) return propBackingFieldName; var i = 0; foreach (var prop in classModel.Properties) { if (!classModel.EnableDataBinding && !(prop.Type is SimpleModel)) continue; if (propertyModel == prop) { i += 1; break; } var backingFieldName = prop.Name.ToBackingField(); if (backingFieldName == propBackingFieldName) i += 1; } if (i <= 1) return propBackingFieldName; return string.Format("{0}{1}", propBackingFieldName, i); }
private IEnumerable<PropertyModel> CreatePropertiesForAttributes(Uri source, TypeModel typeModel, IEnumerable<XmlSchemaObject> items) { var properties = new List<PropertyModel>(); foreach (var item in items) { var attribute = item as XmlSchemaAttribute; if (attribute != null) { if (attribute.Use != XmlSchemaUse.Prohibited) { var attributeQualifiedName = attribute.AttributeSchemaType.QualifiedName; if (attributeQualifiedName.IsEmpty) { attributeQualifiedName = attribute.QualifiedName; if (attributeQualifiedName.IsEmpty || attributeQualifiedName.Namespace == "") { // inner type, have to generate a type name var typeName = ToTitleCase(typeModel.Name) + ToTitleCase(attribute.QualifiedName.Name); attributeQualifiedName = new XmlQualifiedName(typeName, typeModel.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 attributeName = ToTitleCase(attribute.QualifiedName.Name); if (attributeName == typeModel.Name) attributeName += "Property"; // member names cannot be the same as their enclosing type var property = new PropertyModel(_configuration) { OwningType = typeModel, Name = attributeName, XmlSchemaName = attribute.QualifiedName, Type = CreateTypeModel(source, attribute.AttributeSchemaType, attributeQualifiedName), IsAttribute = true, IsNullable = attribute.Use != XmlSchemaUse.Required, DefaultValue = attribute.DefaultValue, Form = attribute.Form == XmlSchemaForm.None ? attribute.GetSchema().AttributeFormDefault : attribute.Form, XmlNamespace = attribute.QualifiedName.Namespace != "" && attribute.QualifiedName.Namespace != typeModel.XmlSchemaName.Namespace ? attribute.QualifiedName.Namespace : null, }; var attributeDocs = GetDocumentation(attribute); property.Documentation.AddRange(attributeDocs); properties.Add(property); } } else { var attributeGroupRef = item as XmlSchemaAttributeGroupRef; if (attributeGroupRef != null) { if (GenerateInterfaces) CreateTypeModel(new Uri(attributeGroupRef.SourceUri), AttributeGroups[attributeGroupRef.RefName], attributeGroupRef.RefName); var groupItems = AttributeGroups[attributeGroupRef.RefName].Attributes; var groupProperties = CreatePropertiesForAttributes(source, typeModel, groupItems.Cast<XmlSchemaObject>()); properties.AddRange(groupProperties); } } } return properties; }
private IEnumerable<PropertyModel> CreatePropertiesForElements(Uri source, TypeModel typeModel, XmlSchemaParticle particle, IEnumerable<Particle> items) { var properties = new List<PropertyModel>(); var order = 0; foreach (var item in items) { PropertyModel property = null; var element = item.XmlParticle as XmlSchemaElement; // ElementSchemaType must be non-null. This is not the case when maxOccurs="0". if (element != null && element.ElementSchemaType != null) { var elementQualifiedName = element.ElementSchemaType.QualifiedName; if (elementQualifiedName.IsEmpty) { elementQualifiedName = element.RefName; if (elementQualifiedName.IsEmpty) { // inner type, have to generate a type name var typeName = ToTitleCase(typeModel.Name) + ToTitleCase(element.QualifiedName.Name); 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)); } } var propertyName = ToTitleCase(element.QualifiedName.Name); if (propertyName == typeModel.Name) propertyName += "Property"; // member names cannot be the same as their enclosing type property = new PropertyModel(_configuration) { OwningType = typeModel, XmlSchemaName = element.QualifiedName, Name = propertyName, Type = CreateTypeModel(source, element.ElementSchemaType, elementQualifiedName), IsNillable = element.IsNillable, IsNullable = item.MinOccurs < 1.0m, IsCollection = item.MaxOccurs > 1.0m || particle.MaxOccurs > 1.0m, // http://msdn.microsoft.com/en-us/library/vstudio/d3hx2s7e(v=vs.100).aspx DefaultValue = element.DefaultValue, Form = element.Form == XmlSchemaForm.None ? element.GetSchema().ElementFormDefault : element.Form, XmlNamespace = element.QualifiedName.Namespace != "" && element.QualifiedName.Namespace != typeModel.XmlSchemaName.Namespace ? element.QualifiedName.Namespace : null, }; } else { var any = item.XmlParticle as XmlSchemaAny; if (any != null) { property = new PropertyModel(_configuration) { OwningType = typeModel, Name = "Any", Type = new SimpleModel(_configuration) { ValueType = (UseXElementForAny ? typeof(XElement) : typeof(XmlElement)), UseDataTypeAttribute = false }, IsNullable = item.MinOccurs < 1.0m, IsCollection = item.MaxOccurs > 1.0m || particle.MaxOccurs > 1.0m, // http://msdn.microsoft.com/en-us/library/vstudio/d3hx2s7e(v=vs.100).aspx IsAny = true, }; } else { var groupRef = item.XmlParticle as XmlSchemaGroupRef; if (groupRef != null) { if (GenerateInterfaces) CreateTypeModel(new Uri(groupRef.SourceUri), Groups[groupRef.RefName], groupRef.RefName); var groupItems = GetElements(groupRef.Particle); var groupProperties = CreatePropertiesForElements(source, typeModel, item.XmlParticle, groupItems); properties.AddRange(groupProperties); } } } // Discard duplicate property names. This is most likely due to: // - Choice or // - Element and attribute with the same name if (property != null && !properties.Any(p => p.Name == property.Name)) { var itemDocs = GetDocumentation(item.XmlParticle); property.Documentation.AddRange(itemDocs); if (EmitOrder) property.Order = order++; property.IsDeprecated = itemDocs.Any(d => d.Text.StartsWith("DEPRECATED")); properties.Add(property); } } return properties; }
// ReSharper disable once FunctionComplexityOverflow private TypeModel CreateTypeModel(Uri source, XmlSchemaAnnotated type, XmlQualifiedName qualifiedName) { TypeModel typeModel; if (!qualifiedName.IsEmpty && Types.TryGetValue(qualifiedName, out typeModel)) return typeModel; if (source == null) throw new ArgumentNullException("source"); var namespaceModel = CreateNamespaceModel(source, qualifiedName); var docs = GetDocumentation(type); var group = type as XmlSchemaGroup; if (group != null) { var name = "I" + ToTitleCase(qualifiedName.Name); if (namespaceModel != null) name = namespaceModel.GetUniqueTypeName(name); var interfaceModel = new InterfaceModel(_configuration) { Name = name, Namespace = namespaceModel, XmlSchemaName = qualifiedName }; interfaceModel.Documentation.AddRange(docs); if (namespaceModel != null) namespaceModel.Types[name] = interfaceModel; if (!qualifiedName.IsEmpty) Types[qualifiedName] = interfaceModel; var particle = group.Particle; var items = GetElements(particle); var properties = CreatePropertiesForElements(source, interfaceModel, particle, items.Where(i => !(i.XmlParticle is XmlSchemaGroupRef))); interfaceModel.Properties.AddRange(properties); var interfaces = items.Select(i => i.XmlParticle).OfType<XmlSchemaGroupRef>() .Select(i => (InterfaceModel)CreateTypeModel(new Uri(i.SourceUri), Groups[i.RefName], i.RefName)); interfaceModel.Interfaces.AddRange(interfaces); return interfaceModel; } var attributeGroup = type as XmlSchemaAttributeGroup; if (attributeGroup != null) { var name = "I" + ToTitleCase(qualifiedName.Name); if (namespaceModel != null) name = namespaceModel.GetUniqueTypeName(name); var interfaceModel = new InterfaceModel(_configuration) { Name = name, Namespace = namespaceModel, XmlSchemaName = qualifiedName }; interfaceModel.Documentation.AddRange(docs); if (namespaceModel != null) namespaceModel.Types[name] = interfaceModel; if (!qualifiedName.IsEmpty) Types[qualifiedName] = interfaceModel; var items = attributeGroup.Attributes; var properties = CreatePropertiesForAttributes(source, interfaceModel, items.OfType<XmlSchemaAttribute>()); interfaceModel.Properties.AddRange(properties); var interfaces = items.OfType<XmlSchemaAttributeGroupRef>() .Select(a => (InterfaceModel)CreateTypeModel(new Uri(a.SourceUri), AttributeGroups[a.RefName], a.RefName)); interfaceModel.Interfaces.AddRange(interfaces); return interfaceModel; } var complexType = type as XmlSchemaComplexType; if (complexType != null) { var name = ToTitleCase(qualifiedName.Name); if (namespaceModel != null) name = namespaceModel.GetUniqueTypeName(name); var classModel = new ClassModel(_configuration) { Name = name, Namespace = namespaceModel, XmlSchemaName = qualifiedName, XmlSchemaType = complexType, IsAbstract = complexType.IsAbstract, IsAnonymous = complexType.QualifiedName.Name == "", IsMixed = complexType.IsMixed, IsSubstitution = complexType.Parent is XmlSchemaElement && !((XmlSchemaElement)complexType.Parent).SubstitutionGroup.IsEmpty, EnableDataBinding = EnableDataBinding, }; classModel.Documentation.AddRange(docs); if (namespaceModel != null) { namespaceModel.Types[classModel.Name] = classModel; } if (!qualifiedName.IsEmpty) Types[qualifiedName] = classModel; if (complexType.BaseXmlSchemaType != null && complexType.BaseXmlSchemaType.QualifiedName != AnyType) { var baseModel = CreateTypeModel(source, complexType.BaseXmlSchemaType, complexType.BaseXmlSchemaType.QualifiedName); classModel.BaseClass = baseModel; if (baseModel is ClassModel) ((ClassModel)classModel.BaseClass).DerivedTypes.Add(classModel); } XmlSchemaParticle particle = null; if (classModel.BaseClass != null) { if (complexType.ContentModel.Content is XmlSchemaComplexContentExtension) particle = ((XmlSchemaComplexContentExtension)complexType.ContentModel.Content).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 XmlSchemaComplexContentRestriction) // particle = ((XmlSchemaComplexContentRestriction)complexType.ContentModel.Content).Particle; } else particle = complexType.ContentTypeParticle; var items = GetElements(particle); var properties = CreatePropertiesForElements(source, classModel, particle, items); classModel.Properties.AddRange(properties); if (GenerateInterfaces) { var interfaces = items.Select(i => i.XmlParticle).OfType<XmlSchemaGroupRef>() .Select(i => (InterfaceModel)CreateTypeModel(new Uri(i.SourceUri), Groups[i.RefName], i.RefName)); classModel.Interfaces.AddRange(interfaces); } XmlSchemaObjectCollection attributes = null; if (classModel.BaseClass != null) { if (complexType.ContentModel.Content is XmlSchemaComplexContentExtension) attributes = ((XmlSchemaComplexContentExtension)complexType.ContentModel.Content).Attributes; else if (complexType.ContentModel.Content is XmlSchemaSimpleContentExtension) attributes = ((XmlSchemaSimpleContentExtension)complexType.ContentModel.Content).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 if (complexType.ContentModel.Content is XmlSchemaComplexContentRestriction) // attributes = ((XmlSchemaComplexContentRestriction)complexType.ContentModel.Content).Attributes; //else if (complexType.ContentModel.Content is XmlSchemaSimpleContentRestriction) // attributes = ((XmlSchemaSimpleContentRestriction)complexType.ContentModel.Content).Attributes; } else attributes = complexType.Attributes; if (attributes != null) { var attributeProperties = CreatePropertiesForAttributes(source, classModel, attributes.Cast<XmlSchemaObject>()); classModel.Properties.AddRange(attributeProperties); if (GenerateInterfaces) { var attributeInterfaces = attributes.OfType<XmlSchemaAttributeGroupRef>() .Select(i => (InterfaceModel)CreateTypeModel(new Uri(i.SourceUri), AttributeGroups[i.RefName], i.RefName)); classModel.Interfaces.AddRange(attributeInterfaces); } } if (complexType.AnyAttribute != null) { var property = new PropertyModel(_configuration) { OwningType = classModel, Name = "AnyAttribute", Type = new SimpleModel(_configuration) { ValueType = typeof(XmlAttribute), UseDataTypeAttribute = false }, IsAttribute = true, IsCollection = true, IsAny = true }; var attributeDocs = GetDocumentation(complexType.AnyAttribute); property.Documentation.AddRange(attributeDocs); classModel.Properties.Add(property); } return classModel; } var simpleType = type as XmlSchemaSimpleType; if (simpleType != null) { var restrictions = new List<RestrictionModel>(); var typeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction; if (typeRestriction != null) { var enumFacets = typeRestriction.Facets.OfType<XmlSchemaEnumerationFacet>().ToList(); var isEnum = (enumFacets.Count == typeRestriction.Facets.Count && enumFacets.Count != 0) || (EnumTypes.Contains(typeRestriction.BaseTypeName.Name) && enumFacets.Any()); if (isEnum) { // we got an enum var name = ToTitleCase(qualifiedName.Name); if (namespaceModel != null) name = namespaceModel.GetUniqueTypeName(name); var enumModel = new EnumModel(_configuration) { Name = name, Namespace = namespaceModel, XmlSchemaName = qualifiedName, XmlSchemaType = simpleType, }; enumModel.Documentation.AddRange(docs); foreach (var facet in enumFacets.DistinctBy(f => f.Value)) { var value = new EnumValueModel { Name = ToTitleCase(facet.Value).ToNormalizedEnumName(), Value = facet.Value }; var valueDocs = GetDocumentation(facet); value.Documentation.AddRange(valueDocs); var deprecated = facet.Annotation != null && facet.Annotation.Items.OfType<XmlSchemaAppInfo>() .Any(a => a.Markup.Any(m => m.Name == "annox:annotate" && m.HasChildNodes && m.FirstChild.Name == "jl:Deprecated")); value.IsDeprecated = deprecated; enumModel.Values.Add(value); } if (namespaceModel != null) { namespaceModel.Types[enumModel.Name] = enumModel; } if (!qualifiedName.IsEmpty) Types[qualifiedName] = enumModel; return enumModel; } restrictions = GetRestrictions(typeRestriction.Facets.Cast<XmlSchemaFacet>(), simpleType).Where(r => r != null).Sanitize().ToList(); } var simpleModelName = ToTitleCase(qualifiedName.Name); if (namespaceModel != null) simpleModelName = namespaceModel.GetUniqueTypeName(simpleModelName); var simpleModel = new SimpleModel(_configuration) { Name = simpleModelName, Namespace = namespaceModel, XmlSchemaName = qualifiedName, XmlSchemaType = simpleType, ValueType = simpleType.Datatype.GetEffectiveType(_configuration), }; simpleModel.Documentation.AddRange(docs); simpleModel.Restrictions.AddRange(restrictions); if (namespaceModel != null) { namespaceModel.Types[simpleModel.Name] = simpleModel; } if (!qualifiedName.IsEmpty) Types[qualifiedName] = simpleModel; return simpleModel; } throw new Exception(string.Format("Cannot build declaration for {0}", qualifiedName)); }