// -------------------------------------------------------------------------------------------------------------------- // <copyright file="Net30Extension.cs" company="Xsd2Code"> // N/A // </copyright> // <summary> // Implements code generation extension for .Net Framework 3.0 // </summary> // <remarks> // Updated 2010-01-20 Deerwood McCord Jr. Cleaned CodeSnippetStatements by replacing with specific CodeDom Expressions // </remarks> // -------------------------------------------------------------------------------------------------------------------- namespace Xsd2Code.Library.Extensions { using System.CodeDom; using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Xml.Schema; using Helpers; /// <summary> /// Implements code generation extension for .Net Framework 3.0 /// </summary> [CodeExtension(TargetFramework.Net30)] public class Net30Extension : CodeExtension { #region Private Fields /// <summary> /// List the properties that will change to auto properties /// </summary> private readonly List<CodeMemberProperty> autoPropertyListField = new List<CodeMemberProperty>(); /// <summary> /// List the fields to be deleted /// </summary> private readonly List<CodeMemberField> fieldListToRemoveField = new List<CodeMemberField>(); /// <summary> /// List fields that require an initialization in the constructor /// </summary> private readonly List<string> fieldWithAssignementInCtorListField = new List<string>(); #endregion #region Protected methods /// <summary> /// Processes the class. /// </summary> /// <param name="codeNamespace">The code namespace.</param> /// <param name="schema">The input xsd schema.</param> /// <param name="type">Represents a type declaration for a class, structure, interface, or enumeration</param> protected override void ProcessClass(CodeNamespace codeNamespace, XmlSchema schema, CodeTypeDeclaration type) { this.autoPropertyListField.Clear(); this.fieldListToRemoveField.Clear(); this.fieldWithAssignementInCtorListField.Clear(); // looks for properties that can not become automatic property CodeConstructor ctor = null; foreach (CodeTypeMember member in type.Members) { if (member is CodeConstructor) ctor = member as CodeConstructor; } if (ctor != null) { foreach (var statement in ctor.Statements) { var codeAssignStatement = statement as CodeAssignStatement; if (codeAssignStatement == null) continue; var code = codeAssignStatement.Left as CodeFieldReferenceExpression; if (code != null) { this.fieldWithAssignementInCtorListField.Add(code.FieldName); } } } base.ProcessClass(codeNamespace, schema, type); // generate automatic properties this.GenerateAutomaticProperties(type); } /// <summary> /// Create data contract attribute /// </summary> /// <param name="type">Code type declaration</param> /// <param name="schema">XML schema</param> protected override void CreateDataContractAttribute(CodeTypeDeclaration type, XmlSchema schema) { base.CreateDataContractAttribute(type, schema); if (GeneratorContext.GeneratorParams.GenerateDataContracts) { var attributeType = new CodeTypeReference("System.Runtime.Serialization.DataContractAttribute"); var codeAttributeArgument = new List<CodeAttributeArgument>(); //var typeName = string.Concat('"', type.Name, '"'); //codeAttributeArgument.Add(new CodeAttributeArgument("Name", new CodeSnippetExpression(typeName))); codeAttributeArgument.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(type.Name))); if (!string.IsNullOrEmpty(schema.TargetNamespace)) { //var targetNamespace = string.Concat('\"', schema.TargetNamespace, '\"'); //codeAttributeArgument.Add(new CodeAttributeArgument("Namespace", new CodeSnippetExpression(targetNamespace))); codeAttributeArgument.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(schema.TargetNamespace))); } type.CustomAttributes.Add(new CodeAttributeDeclaration(attributeType, codeAttributeArgument.ToArray())); } } /// <summary> /// Creates the data member attribute. /// </summary> /// <param name="prop">Represents a declaration for a property of a type.</param> protected override void CreateDataMemberAttribute(CodeMemberProperty prop) { base.CreateDataMemberAttribute(prop); if (GeneratorContext.GeneratorParams.GenerateDataContracts) { var attrib = new CodeTypeReference("System.Runtime.Serialization.DataMemberAttribute"); prop.CustomAttributes.Add(new CodeAttributeDeclaration(attrib)); } } /// <summary> /// Import namespaces /// </summary> /// <param name="code">Code namespace</param> protected override void ImportNamespaces(CodeNamespace code) { base.ImportNamespaces(code); if (GeneratorContext.GeneratorParams.GenerateDataContracts) code.Imports.Add(new CodeNamespaceImport("System.Runtime.Serialization")); } /// <summary> /// Property process /// </summary> /// <param name="type">Represents a type declaration for a class, structure, interface, or enumeration</param> /// <param name="ns">The ns.</param> /// <param name="member">Type members include fields, methods, properties, constructors and nested types</param> /// <param name="xmlElement">Represent the root element in schema</param> /// <param name="schema">XML Schema</param> protected override void ProcessProperty(CodeTypeDeclaration type, CodeNamespace ns, CodeTypeMember member, XmlSchemaElement xmlElement, XmlSchema schema) { // Get now if property is array before base.ProcessProperty call. var prop = (CodeMemberProperty)member; base.ProcessProperty(type, ns, member, xmlElement, schema); // Generate automatic properties. if (GeneratorContext.GeneratorParams.Language == GenerationLanguage.CSharp) { if (GeneratorContext.GeneratorParams.AutomaticProperties) { if (!this.IsComplexType(prop.Type, ns)) { // Exclude collection type if (collectionTypesFields.IndexOf(prop.Name) == -1) { // Get private fieldName var propReturnStatment = prop.GetStatements[0] as CodeMethodReturnStatement; if (propReturnStatment != null) { var field = propReturnStatment.Expression as CodeFieldReferenceExpression; if (field != null) { // Check if private field don't need initialisation in ctor (defaut value). if (this.fieldWithAssignementInCtorListField.FindIndex(p => p == field.FieldName) == -1) { this.autoPropertyListField.Add(member as CodeMemberProperty); } } } } } } } } /// <summary> /// process Fields. /// </summary> /// <param name="member">CodeTypeMember member</param> /// <param name="ctor">CodeMemberMethod constructor</param> /// <param name="ns">CodeNamespace XSD</param> /// <param name="addedToConstructor">Indicates if create a new constructor</param> protected override void ProcessFields(CodeTypeMember member, CodeMemberMethod ctor, CodeNamespace ns, ref bool addedToConstructor) { // Get now if filed is array before base.ProcessProperty call. var field = (CodeMemberField)member; bool isArray = field.Type.ArrayElementType != null; base.ProcessFields(member, ctor, ns, ref addedToConstructor); // Generate automatic properties. if (GeneratorContext.GeneratorParams.Language == GenerationLanguage.CSharp) { if (GeneratorContext.GeneratorParams.AutomaticProperties) { if (!isArray) { if (!this.IsComplexType(field.Type, ns)) { // If this field is not assigned in ctor, add it in remove list. // with automatic property, don't need to keep private field. if (this.fieldWithAssignementInCtorListField.FindIndex(p => p == field.Name) == -1) { this.fieldListToRemoveField.Add(field); } } } } } } #endregion #region static methods /// <summary> /// Outputs the attribute argument. /// </summary> /// <param name="arg">Represents an argument used in a metadata attribute declaration.</param> /// <returns>transform attribute into srting</returns> private static string AttributeArgumentToString(CodeAttributeArgument arg) { var strWriter = new StringWriter(); var provider = CodeDomProviderFactory.GetProvider(GeneratorContext.GeneratorParams.Language); if (!string.IsNullOrEmpty(arg.Name)) { strWriter.Write(arg.Name); strWriter.Write("="); } provider.GenerateCodeFromExpression(arg.Value, strWriter, new CodeGeneratorOptions()); var strrdr = new StringReader(strWriter.ToString()); return strrdr.ReadToEnd(); } #endregion /// <summary> /// Outputs the attribute argument. /// </summary> /// <param name="arg">Represents an argument used in a metadata attribute declaration.</param> /// <returns>transform attribute into srting</returns> private static string ExpressionToString(CodeExpression arg) { var strWriter = new StringWriter(); var provider = CodeDomProviderFactory.GetProvider(GeneratorContext.GeneratorParams.Language); provider.GenerateCodeFromExpression(arg, strWriter, new CodeGeneratorOptions()); var strrdr = new StringReader(strWriter.ToString()); return strrdr.ReadToEnd(); } #region Private methods /// <summary> /// Generates the automatic properties. /// </summary> /// <param name="type">Represents a type declaration for a class, structure, interface, or enumeration.</param> private void GenerateAutomaticProperties(CodeTypeDeclaration type) { if (Equals(GeneratorContext.GeneratorParams.Language, GenerationLanguage.CSharp)) { // If databinding is disable, use automatic property if (GeneratorContext.GeneratorParams.AutomaticProperties) { foreach (var item in this.autoPropertyListField) { var cm = new CodeSnippetTypeMember(); bool transformToAutomaticproperty = true; var attributesString = new List<string>(); foreach (var attribute in item.CustomAttributes) { var attrib = attribute as CodeAttributeDeclaration; if (attrib != null) { // Don't transform property with default value. if (attrib.Name == "System.ComponentModel.DefaultValueAttribute") { transformToAutomaticproperty = false; } else { string attributesArgument = string.Empty; foreach (var arg in attrib.Arguments) { var argument = arg as CodeAttributeArgument; if (argument != null) { attributesArgument += AttributeArgumentToString(argument); } } attributesString.Add(string.Format("[{0}({1})]", attrib.Name, attributesArgument)); } } } if (transformToAutomaticproperty) { foreach (var attribute in attributesString) { cm.Text += " " + attribute + "\n"; } var ct = new CodeTypeReferenceExpression(item.Type); var prop = ExpressionToString(ct); var text = string.Format(" public {0} {1} ", prop, item.Name); cm.Text += string.Concat(text, "{get; set;}\n"); cm.Comments.AddRange(item.Comments); type.Members.Add(cm); type.Members.Remove(item); } } // Now remove all private fileds foreach (var item in this.fieldListToRemoveField) { type.Members.Remove(item); } } } } #endregion } }