/// <summary> /// Outputs a generated model to a string builder. /// </summary> /// <param name="sb">The string builder.</param> /// <param name="typeModel">The model to generate.</param> public void Generate(StringBuilder sb, TypeModel typeModel) { WriteHeader(sb); foreach (var t in TypesUsing) sb.AppendFormat("using {0};\n", t); sb.Append("\n"); sb.AppendFormat("namespace {0}\n", GetModelsNamespace()); sb.Append("{\n"); WriteContentType(sb, typeModel); sb.Append("}\n"); }
public void ModelsUsingAttribute() { // Umbraco returns nice, pascal-cased names var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var types = new[] { type1 }; var code1 = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; "} }; var code2 = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; [assembly:ModelsUsing(""Foo.Bar.Nil"")] "} }; var parseResult = new CodeParser().Parse(code1); var builder = new TextBuilder(types, parseResult); var count = builder.Using.Count; parseResult = new CodeParser().Parse(code2); builder = new TextBuilder(types, parseResult); Assert.AreEqual(count + 1, builder.Using.Count); Assert.IsTrue(builder.Using.Contains("Foo.Bar.Nil")); }
public void ContentTypeCustomBaseClass() { var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var type2 = new TypeModel { Id = 2, Alias = "type2", ClrName = "Type2", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var type3 = new TypeModel { Id = 3, Alias = "type3", ClrName = "Type3", ParentId = 1, BaseType = type1, ItemType = TypeModel.ItemTypes.Content, }; var types = new[] { type1, type2, type3 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; using Dang; namespace Dang { public abstract class MyModelBase : PublishedContentModel { public MyModelBase(IPublishedContent content) : base(content) { } } public abstract class MyType1 : Type1 { public MyType1(IPublishedContent content) : base(content) { } } public partial class Type1 {} public partial class Type2 : MyModelBase {} public partial class Type3 : MyType1 { } } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.AreEqual(3, btypes.Count); var btype1 = btypes[0]; Assert.AreEqual("Type1", btype1.ClrName); var btype2 = btypes[1]; Assert.AreEqual("Type2", btype2.ClrName); var btype3 = btypes[2]; Assert.AreEqual("Type3", btype3.ClrName); Assert.IsFalse(btype1.HasBase); Assert.IsTrue(btype2.HasBase); Assert.IsTrue(btype3.HasBase); var sb = new StringBuilder(); builder.Generate(sb, btype3); var gen = sb.ToString(); Console.WriteLine(gen); Assert.Greater(gen.IndexOf("public partial class Type3\n"), 0); Assert.Greater(0, gen.IndexOf("public partial class Type3 : ")); }
public void ContentTypeRenameOnClass() { // Umbraco returns nice, pascal-cased names var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var type2 = new TypeModel { Id = 2, Alias = "type2", ClrName = "Type2", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var types = new[] { type1, type2 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; namespace Models { [ImplementContentType(""type1"")] public partial class Renamed1 {} } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.IsFalse(parseResult.IsIgnored("type1")); Assert.IsFalse(parseResult.IsIgnored("type2")); Assert.IsTrue(parseResult.IsContentRenamed("type1")); Assert.IsFalse(parseResult.IsContentRenamed("type2")); Assert.AreEqual("Renamed1", parseResult.ContentClrName("type1")); Assert.IsNull(parseResult.ContentClrName("type2")); Assert.AreEqual(2, btypes.Count); Assert.IsFalse(btypes[0].IsContentIgnored); Assert.IsFalse(btypes[1].IsContentIgnored); Assert.AreEqual("Renamed1", btypes[0].ClrName); Assert.AreEqual("Type2", btypes[1].ClrName); }
public void ContentTypeIgnoreMixin() { // Umbraco returns nice, pascal-cased names var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var type2 = new TypeModel { Id = 2, Alias = "type2", ClrName = "Type2", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type2.MixinTypes.Add(type1); var types = new[] { type1, type2 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; [assembly:IgnoreContentType(""type1"")] "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.IsTrue(parseResult.IsIgnored("type1")); Assert.IsFalse(parseResult.IsIgnored("type2")); Assert.AreEqual(2, btypes.Count); Assert.AreEqual("type1", btypes[0].Alias); Assert.AreEqual("type2", btypes[1].Alias); Assert.IsTrue(btypes[0].IsContentIgnored); Assert.IsFalse(btypes[1].IsContentIgnored); Assert.AreEqual(0, btypes[1].DeclaringInterfaces.Count); }
private void WriteContentTypeProperties(StringBuilder sb, TypeModel type) { var staticMixinGetters = Configuration.Config.StaticMixinGetters; // write the properties foreach (var prop in type.Properties.Where(x => !x.IsIgnored).OrderBy(x => x.ClrName)) WriteProperty(sb, type, prop, staticMixinGetters && type.IsMixin ? type.ClrName : null); // no need to write the parent properties since we inherit from the parent // and the parent defines its own properties. need to write the mixins properties // since the mixins are only interfaces and we have to provide an implementation. // write the mixins properties foreach (var mixinType in type.ImplementingInterfaces.OrderBy(x => x.ClrName)) foreach (var prop in mixinType.Properties.Where(x => !x.IsIgnored).OrderBy(x => x.ClrName)) if (staticMixinGetters) WriteMixinProperty(sb, prop, mixinType.ClrName); else WriteProperty(sb, mixinType, prop); }
/// <summary> /// Recursively collects all types inherited, or implemented as interfaces, by a specified type. /// </summary> /// <param name="types">The collection.</param> /// <param name="type">The type.</param> /// <remarks>Includes the specified type.</remarks> internal static void CollectImplems(ICollection<TypeModel> types, TypeModel type) { if (!type.IsContentIgnored && types.Contains(type) == false) types.Add(type); if (type.BaseType != null && !type.BaseType.IsContentIgnored) CollectImplems(types, type.BaseType); foreach (var mixin in type.MixinTypes.Where(x => !x.IsContentIgnored)) CollectImplems(types, mixin); }
public void PropertyTypeImplementOnInterface() { var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type1.Properties.Add(new PropertyModel { Alias = "prop1a", ClrName = "Prop1a", ClrType = typeof(string), }); type1.Properties.Add(new PropertyModel { Alias = "prop1b", ClrName = "Prop1b", ClrType = typeof(string), }); type1.Properties.Add(new PropertyModel { Alias = "prop1c", ClrName = "Prop1c", ClrType = typeof(string), }); var type2 = new TypeModel { Id = 1, Alias = "type2", ClrName = "Type2", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type2.Properties.Add(new PropertyModel { Alias = "prop2", ClrName = "Prop2", ClrType = typeof(string), }); var types = new[] { type1, type2 }; type2.MixinTypes.Add(type1); type1.IsMixin = true; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; using Dang; namespace Dang { // both attributes are ignored on the interface - do it on the class //[RenamePropertyType(""prop1b"", ""Prop1x"")] //[IgnorePropertyType(""prop1c"")] public partial interface IType1 { // attribute is not supported (ie ignored) here //[ImplementPropertyType(""prop1a"")] // have to do this (see notes in Type1) public string Foo { get; } } // both attributes on the class will be mirrored on the interface [RenamePropertyType(""prop1b"", ""Prop1x"")] [IgnorePropertyType(""prop1c"")] public partial class Type1 { // and then, // - property will NOT be implemented in Type2, MUST be done manually // - property will NOT be mirrored on the interface, MUST be done manually [ImplementPropertyType(""prop1a"")] public string Foo { get { return string.Empty; } } } public partial class Type2 { // have to do this (see notes in Type1) [ImplementPropertyType(""prop1a"")] public string Foo { get { return string.Empty; } } } } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.AreEqual(2, btypes.Count); var btype1 = btypes[0]; Assert.AreEqual("Type1", btype1.ClrName); var btype2 = btypes[1]; Assert.AreEqual("Type2", btype2.ClrName); var sb = new StringBuilder(); builder.Generate(sb, btype1); var gen = sb.ToString(); Console.WriteLine(gen); // contains Assert.Greater(gen.IndexOf("string Prop1x"), 0); // does not contain Assert.Greater(0, gen.IndexOf("string Prop1a")); Assert.Greater(0, gen.IndexOf("string Prop1b")); Assert.Greater(0, gen.IndexOf("string Prop1c")); sb.Clear(); builder.Generate(sb, btype2); gen = sb.ToString(); Console.WriteLine(gen); // contains Assert.Greater(gen.IndexOf("string Prop2"), 0); Assert.Greater(gen.IndexOf("string Prop1x"), 0); // does not contain Assert.Greater(0, gen.IndexOf("string Prop1a")); Assert.Greater(0, gen.IndexOf("string Prop1b")); Assert.Greater(0, gen.IndexOf("string Prop1c")); }
public void PropertyTypeRenameOnPropertyInherit() { // Umbraco returns nice, pascal-cased names var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type1.Properties.Add(new PropertyModel { Alias = "prop1", ClrName = "Prop1", ClrType = typeof(string), }); var types = new[] { type1 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; namespace Models { public class Type2 { [ImplementPropertyType(""prop1"")] public string Renamed1 { get { return """"; } } } public partial class Type1 : Type2 { } } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.IsTrue(parseResult.IsPropertyIgnored("Type1", "prop1")); Assert.AreEqual(1, btypes.Count); Assert.IsTrue(btypes[0].Properties[0].IsIgnored); }
private void WriteProperty(StringBuilder sb, TypeModel type, PropertyModel property, string mixinClrName = null) { var mixinStatic = mixinClrName != null; sb.Append("\n"); if (property.Errors != null) { sb.Append("\t\t/*\n"); sb.Append("\t\t * THIS PROPERTY CANNOT BE IMPLEMENTED, BECAUSE:\n"); sb.Append("\t\t *\n"); var first = true; foreach (var error in property.Errors) { if (first) { first = false; } else { sb.Append("\t\t *\n"); } foreach (var s in SplitError(error)) { sb.Append("\t\t * "); sb.Append(s); sb.Append("\n"); } } sb.Append("\t\t *\n"); sb.Append("\n"); } // Adds xml summary to each property containing // property name and property description if (!string.IsNullOrWhiteSpace(property.Name) || !string.IsNullOrWhiteSpace(property.Description)) { sb.Append("\t\t///<summary>\n"); if (!string.IsNullOrWhiteSpace(property.Description)) { sb.AppendFormat("\t\t/// {0}: {1}\n", XmlCommentString(property.Name), XmlCommentString(property.Description)); } else { sb.AppendFormat("\t\t/// {0}\n", XmlCommentString(property.Name)); } sb.Append("\t\t///</summary>\n"); } sb.AppendFormat("\t\t[ImplementPropertyType(\"{0}\")]\n", property.Alias); if (mixinStatic) { sb.Append("\t\tpublic "); WriteClrType(sb, property.ClrType); sb.AppendFormat(" {0}\n\t\t{{\n\t\t\tget {{ return {1}(this); }}\n\t\t}}\n", property.ClrName, MixinStaticGetterName(property.ClrName)); } else { sb.Append("\t\tpublic "); WriteClrType(sb, property.ClrType); sb.AppendFormat(" {0}\n\t\t{{\n\t\t\tget {{ return this.GetPropertyValue", property.ClrName); if (property.ClrType != typeof(object)) { sb.Append("<"); WriteClrType(sb, property.ClrType); sb.Append(">"); } sb.AppendFormat("(\"{0}\"); }}\n\t\t}}\n", property.Alias); } if (property.Errors != null) { sb.Append("\n"); sb.Append("\t\t *\n"); sb.Append("\t\t */\n"); } if (!mixinStatic) { return; } var mixinStaticGetterName = MixinStaticGetterName(property.ClrName); if (type.StaticMixinMethods.Contains(mixinStaticGetterName)) { return; } sb.Append("\n"); if (!string.IsNullOrWhiteSpace(property.Name)) { sb.AppendFormat("\t\t/// <summary>Static getter for {0}</summary>\n", XmlCommentString(property.Name)); } sb.Append("\t\tpublic static "); WriteClrType(sb, property.ClrType); sb.AppendFormat(" {0}(I{1} that) {{ return that.GetPropertyValue", mixinStaticGetterName, mixinClrName); if (property.ClrType != typeof(object)) { sb.Append("<"); WriteClrType(sb, property.ClrType); sb.Append(">"); } sb.AppendFormat("(\"{0}\"); }}\n", property.Alias); }
private void WriteContentType(StringBuilder sb, TypeModel type) { string sep; if (type.IsMixin) { // write the interface declaration sb.AppendFormat("\t// Mixin content Type {0} with alias \"{1}\"\n", type.Id, type.Alias); if (!string.IsNullOrWhiteSpace(type.Name)) { sb.AppendFormat("\t/// <summary>{0}</summary>\n", XmlCommentString(type.Name)); } sb.AppendFormat("\tpublic partial interface I{0}", type.ClrName); var implements = type.BaseType == null || type.BaseType.IsContentIgnored ? (type.HasBase ? null : "PublishedContent") : type.BaseType.ClrName; if (implements != null) { sb.AppendFormat(" : I{0}", implements); } // write the mixins sep = implements == null ? ":" : ","; foreach (var mixinType in type.DeclaringInterfaces.OrderBy(x => x.ClrName)) { sb.AppendFormat("{0} I{1}", sep, mixinType.ClrName); sep = ","; } sb.Append("\n\t{\n"); // write the properties - only the local (non-ignored) ones, we're an interface var more = false; foreach (var prop in type.Properties.Where(x => !x.IsIgnored).OrderBy(x => x.ClrName)) { if (more) { sb.Append("\n"); } more = true; WriteInterfaceProperty(sb, prop); } sb.Append("\t}\n\n"); } // write the class declaration if (type.IsRenamed) { sb.AppendFormat("\t// Content Type {0} with alias \"{1}\"\n", type.Id, type.Alias); } if (!string.IsNullOrWhiteSpace(type.Name)) { sb.AppendFormat("\t/// <summary>{0}</summary>\n", XmlCommentString(type.Name)); } // cannot do it now. see note in ImplementContentTypeAttribute //if (!type.HasImplement) // sb.AppendFormat("\t[ImplementContentType(\"{0}\")]\n", type.Alias); sb.AppendFormat("\t[PublishedContentModel(\"{0}\")]\n", type.Alias); sb.AppendFormat("\tpublic partial class {0}", type.ClrName); var inherits = type.HasBase ? null // has its own base already : (type.BaseType == null || type.BaseType.IsContentIgnored ? GetModelsBaseClassName() : type.BaseType.ClrName); if (inherits != null) { sb.AppendFormat(" : {0}", inherits); } sep = inherits == null ? ":" : ","; if (type.IsMixin) { // if it's a mixin it implements its own interface sb.AppendFormat("{0} I{1}", sep, type.ClrName); } else { // write the mixins, if any, as interfaces // only if not a mixin because otherwise the interface already has them already foreach (var mixinType in type.DeclaringInterfaces.OrderBy(x => x.ClrName)) { sb.AppendFormat("{0} I{1}", sep, mixinType.ClrName); sep = ","; } } // begin class body sb.Append("\n\t{\n"); // write the constants // as 'new' since parent has its own - or maybe not - disable warning sb.Append("#pragma warning disable 0109 // new is redundant\n"); sb.AppendFormat("\t\tpublic new const string ModelTypeAlias = \"{0}\";\n", type.Alias); sb.AppendFormat("\t\tpublic new const PublishedItemType ModelItemType = PublishedItemType.{0};\n", type.ItemType); sb.Append("#pragma warning restore 0109\n\n"); // write the ctor if (!type.HasCtor) { sb.AppendFormat("\t\tpublic {0}(IPublishedContent content)\n\t\t\t: base(content)\n\t\t{{ }}\n\n", type.ClrName); } // write the static methods // as 'new' since parent has its own - or maybe not - disable warning sb.Append("#pragma warning disable 0109 // new is redundant\n"); sb.Append("\t\tpublic new static PublishedContentType GetModelContentType()\n"); sb.Append("\t\t{\n\t\t\treturn PublishedContentType.Get(ModelItemType, ModelTypeAlias);\n\t\t}\n"); sb.Append("#pragma warning restore 0109\n\n"); sb.AppendFormat("\t\tpublic static PublishedPropertyType GetModelPropertyType<TValue>(Expression<Func<{0}, TValue>> selector)\n", type.ClrName); sb.Append("\t\t{\n\t\t\treturn PublishedContentModelUtility.GetModelPropertyType(GetModelContentType(), selector);\n\t\t}\n"); // write the properties WriteContentTypeProperties(sb, type); // close the class declaration sb.Append("\t}\n"); }
/// <summary> /// Outputs a generated model to a code namespace. /// </summary> /// <param name="ns">The code namespace.</param> /// <param name="typeModel">The model to generate.</param> public void Generate(CodeNamespace ns, TypeModel typeModel) { // what about USING? // what about references? if (typeModel.IsMixin) { var i = new CodeTypeDeclaration("I" + typeModel.ClrName) { IsInterface = true, IsPartial = true, Attributes = MemberAttributes.Public }; i.BaseTypes.Add(typeModel.BaseType == null ? "IPublishedContent" : "I" + typeModel.BaseType.ClrName); foreach (var mixinType in typeModel.DeclaringInterfaces) { i.BaseTypes.Add(mixinType.ClrName); } i.Comments.Add(new CodeCommentStatement($"Mixin content Type {typeModel.Id} with alias \"{typeModel.Alias}\"")); foreach (var propertyModel in typeModel.Properties) { var p = new CodeMemberProperty { Name = propertyModel.ClrName, Type = new CodeTypeReference(propertyModel.ModelClrType), Attributes = MemberAttributes.Public, HasGet = true, HasSet = false }; i.Members.Add(p); } } var c = new CodeTypeDeclaration(typeModel.ClrName) { IsClass = true, IsPartial = true, Attributes = MemberAttributes.Public }; c.BaseTypes.Add(typeModel.BaseType == null ? "PublishedContentModel" : typeModel.BaseType.ClrName); // if it's a missing it implements its own interface if (typeModel.IsMixin) { c.BaseTypes.Add("I" + typeModel.ClrName); } // write the mixins, if any, as interfaces // only if not a mixin because otherwise the interface already has them if (typeModel.IsMixin == false) { foreach (var mixinType in typeModel.DeclaringInterfaces) { c.BaseTypes.Add("I" + mixinType.ClrName); } } foreach (var mixin in typeModel.MixinTypes) { c.BaseTypes.Add("I" + mixin.ClrName); } c.Comments.Add(new CodeCommentStatement($"Content Type {typeModel.Id} with alias \"{typeModel.Alias}\"")); foreach (var propertyModel in typeModel.Properties) { var p = new CodeMemberProperty { Name = propertyModel.ClrName, Type = new CodeTypeReference(propertyModel.ModelClrType), Attributes = MemberAttributes.Public, HasGet = true, HasSet = false }; p.GetStatements.Add(new CodeMethodReturnStatement( // return new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), // this "Value", // .Value new[] // <T> { new CodeTypeReference(propertyModel.ModelClrType) }), new CodeExpression[] // ("alias") { new CodePrimitiveExpression(propertyModel.Alias) }))); c.Members.Add(p); } }
private static IList<TypeModel> GetTypes(PublishedItemType itemType, IContentTypeBase[] contentTypes) { var typeModels = new List<TypeModel>(); // get the types and the properties foreach (var contentType in contentTypes) { var typeModel = new TypeModel { Id = contentType.Id, Alias = contentType.Alias, ClrName = contentType.Alias.ToCleanString(CleanStringType.ConvertCase | CleanStringType.PascalCase), ParentId = contentType.ParentId, Name = contentType.Name, Description = contentType.Description }; switch (itemType) { case PublishedItemType.Content: typeModel.ItemType = TypeModel.ItemTypes.Content; break; case PublishedItemType.Media: typeModel.ItemType = TypeModel.ItemTypes.Media; break; case PublishedItemType.Member: typeModel.ItemType = TypeModel.ItemTypes.Member; break; default: throw new InvalidOperationException(string.Format("Unsupported PublishedItemType \"{0}\".", itemType)); } typeModels.Add(typeModel); var publishedContentType = PublishedContentType.Get(itemType, contentType.Alias); foreach (var propertyType in contentType.PropertyTypes) { var propertyModel = new PropertyModel { Alias = propertyType.Alias, ClrName = propertyType.Alias.ToCleanString(CleanStringType.ConvertCase | CleanStringType.PascalCase), Name = propertyType.Name, Description = propertyType.Description }; var publishedPropertyType = publishedContentType.GetPropertyType(propertyType.Alias); propertyModel.ClrType = publishedPropertyType.ClrType; typeModel.Properties.Add(propertyModel); } } // wire the base types foreach (var typeModel in typeModels.Where(x => x.ParentId > 0)) { typeModel.BaseType = typeModels.SingleOrDefault(x => x.Id == typeModel.ParentId); // Umbraco 7.4 introduces content types containers, so even though ParentId > 0, the parent might // not be a content type - here we assume that BaseType being null while ParentId > 0 means that // the parent is a container (and we don't check). typeModel.IsParent = typeModel.BaseType != null; } // discover mixins foreach (var contentType in contentTypes) { var typeModel = typeModels.SingleOrDefault(x => x.Id == contentType.Id); if (typeModel == null) throw new Exception("Panic: no type model matching content type."); IEnumerable<IContentTypeComposition> compositionTypes; var contentTypeAsMedia = contentType as IMediaType; var contentTypeAsContent = contentType as IContentType; var contentTypeAsMember = contentType as IMemberType; if (contentTypeAsMedia != null) compositionTypes = contentTypeAsMedia.ContentTypeComposition; else if (contentTypeAsContent != null) compositionTypes = contentTypeAsContent.ContentTypeComposition; else if (contentTypeAsMember != null) compositionTypes = contentTypeAsMember.ContentTypeComposition; else throw new Exception(string.Format("Panic: unsupported type \"{0}\".", contentType.GetType().FullName)); foreach (var compositionType in compositionTypes) { var compositionModel = typeModels.SingleOrDefault(x => x.Id == compositionType.Id); if (compositionModel == null) throw new Exception("Panic: composition type does not exist."); if (compositionType.Id == contentType.ParentId) continue; // add to mixins typeModel.MixinTypes.Add(compositionModel); // mark as mixin - as well as parents compositionModel.IsMixin = true; while ((compositionModel = compositionModel.BaseType) != null) compositionModel.IsMixin = true; } } return typeModels; }
private void WriteProperty(StringBuilder sb, TypeModel type, PropertyModel property, string mixinClrName = null) { var mixinStatic = mixinClrName != null; sb.Append("\n"); // Adds xml summary to each property containing // property name and property description if (!string.IsNullOrWhiteSpace(property.Name) || !string.IsNullOrWhiteSpace(property.Description)) { sb.Append("\t\t///<summary>\n"); if (!string.IsNullOrWhiteSpace(property.Description)) sb.AppendFormat("\t\t/// {0}: {1}\n", XmlCommentString(property.Name), XmlCommentString(property.Description)); else sb.AppendFormat("\t\t/// {0}\n", XmlCommentString(property.Name)); sb.Append("\t\t///</summary>\n"); } sb.AppendFormat("\t\t[ImplementPropertyType(\"{0}\")]\n", property.Alias); if (mixinStatic) { sb.Append("\t\tpublic "); WriteClrType(sb, property.ClrType); sb.AppendFormat(" {0}\n\t\t{{\n\t\t\tget {{ return {1}(this); }}\n\t\t}}\n", property.ClrName, MixinStaticGetterName(property.ClrName)); } else { sb.Append("\t\tpublic "); WriteClrType(sb, property.ClrType); sb.AppendFormat(" {0}\n\t\t{{\n\t\t\tget {{ return this.GetPropertyValue", property.ClrName); if (property.ClrType != typeof(object)) { sb.Append("<"); WriteClrType(sb, property.ClrType); sb.Append(">"); } sb.AppendFormat("(\"{0}\"); }}\n\t\t}}\n", property.Alias); } if (!mixinStatic) return; var mixinStaticGetterName = MixinStaticGetterName(property.ClrName); if (type.StaticMixinMethods.Contains(mixinStaticGetterName)) return; sb.Append("\n"); if (!string.IsNullOrWhiteSpace(property.Name)) sb.AppendFormat("\t\t/// <summary>Static getter for {0}</summary>\n", XmlCommentString(property.Name)); sb.Append("\t\tpublic static "); WriteClrType(sb, property.ClrType); sb.AppendFormat(" {0}(I{1} that) {{ return that.GetPropertyValue", mixinStaticGetterName, mixinClrName); if (property.ClrType != typeof(object)) { sb.Append("<"); WriteClrType(sb, property.ClrType); sb.Append(">"); } sb.AppendFormat("(\"{0}\"); }}\n", property.Alias); }
public void MixinPropertyStatic() { Configuration.Config.StaticMixinGetters = true; // try the new way var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, IsMixin = true, }; type1.Properties.Add(new PropertyModel { Alias = "prop1a", ClrName = "Prop1a", ClrType = typeof(string), }); type1.Properties.Add(new PropertyModel { Alias = "prop1b", ClrName = "Prop1b", ClrType = typeof(string), }); var type2 = new TypeModel { Id = 2, Alias = "type2", ClrName = "Type2", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type2.MixinTypes.Add(type1); type2.Properties.Add(new PropertyModel { Alias = "prop2", ClrName = "Prop2", ClrType = typeof(int), }); var types = new[] { type1, type2 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; using Test; namespace Test { public partial class Type1 { public static int GetProp1a(IType1 that) { return that.GetPropertyValue<int>(""prop1a""); } } } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; var sb = new StringBuilder(); foreach (var model in builder.GetModelsToGenerate()) builder.Generate(sb, model); var gen = sb.ToString(); var version = typeof(Builder).Assembly.GetName().Version; var expected = @"//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // // Umbraco.ModelsBuilder v" + version + @" // // Changes to this file will be lost if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Web; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; using Umbraco.ModelsBuilder; using Umbraco.ModelsBuilder.Umbraco; namespace Umbraco.Web.PublishedContentModels { // Mixin content Type 1 with alias ""type1"" public partial interface IType1 : IPublishedContent { string Prop1a { get; } string Prop1b { get; } } [PublishedContentModel(""type1"")] public partial class Type1 : PublishedContentModel, IType1 { #pragma warning disable 0109 // new is redundant public new const string ModelTypeAlias = ""type1""; public new const PublishedItemType ModelItemType = PublishedItemType.Content; #pragma warning restore 0109 public Type1(IPublishedContent content) : base(content) { } #pragma warning disable 0109 // new is redundant public new static PublishedContentType GetModelContentType() { return PublishedContentType.Get(ModelItemType, ModelTypeAlias); } #pragma warning restore 0109 public static PublishedPropertyType GetModelPropertyType<TValue>(Expression<Func<Type1, TValue>> selector) { return PublishedContentModelUtility.GetModelPropertyType(GetModelContentType(), selector); } [ImplementPropertyType(""prop1a"")] public string Prop1a { get { return GetProp1a(this); } } [ImplementPropertyType(""prop1b"")] public string Prop1b { get { return GetProp1b(this); } } public static string GetProp1b(IType1 that) { return that.GetPropertyValue<string>(""prop1b""); } } } //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // // Umbraco.ModelsBuilder v" + version + @" // // Changes to this file will be lost if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Web; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; using Umbraco.ModelsBuilder; using Umbraco.ModelsBuilder.Umbraco; namespace Umbraco.Web.PublishedContentModels { [PublishedContentModel(""type2"")] public partial class Type2 : PublishedContentModel, IType1 { #pragma warning disable 0109 // new is redundant public new const string ModelTypeAlias = ""type2""; public new const PublishedItemType ModelItemType = PublishedItemType.Content; #pragma warning restore 0109 public Type2(IPublishedContent content) : base(content) { } #pragma warning disable 0109 // new is redundant public new static PublishedContentType GetModelContentType() { return PublishedContentType.Get(ModelItemType, ModelTypeAlias); } #pragma warning restore 0109 public static PublishedPropertyType GetModelPropertyType<TValue>(Expression<Func<Type2, TValue>> selector) { return PublishedContentModelUtility.GetModelPropertyType(GetModelContentType(), selector); } [ImplementPropertyType(""prop2"")] public int Prop2 { get { return this.GetPropertyValue<int>(""prop2""); } } [ImplementPropertyType(""prop1a"")] public string Prop1a { get { return Type1.GetProp1a(this); } } [ImplementPropertyType(""prop1b"")] public string Prop1b { get { return Type1.GetProp1b(this); } } } } "; //Console.WriteLine(gen); Assert.AreEqual(expected.ClearLf(), gen); }
public void PropertyTypeRenameOnClass() { // Umbraco returns nice, pascal-cased names var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type1.Properties.Add(new PropertyModel { Alias = "prop1", ClrName = "Prop1", ClrType = typeof(string), }); var types = new[] { type1 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; namespace Models { [RenamePropertyType(""prop1"", ""Renamed1"")] [RenamePropertyType(""prop2"", ""Renamed2"")] public partial class Type1 { } } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.AreEqual("Renamed1", parseResult.PropertyClrName("Type1", "prop1")); Assert.AreEqual("Renamed2", parseResult.PropertyClrName("Type1", "prop2")); Assert.AreEqual(1, btypes.Count); Assert.IsTrue(btypes[0].Properties[0].ClrName == "Renamed1"); }
public void ReferencedAssemblies() { var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; var types = new[] { type1 }; var code1 = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; public partial class Type1 {} "} }; Assert.IsFalse(new CodeParser().Parse(code1).HasContentBase("Type1")); var code2 = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; public partial class Type1 : IHasXmlNode {} "} }; // assumes base is IHasXmlNode (cannot be verified...) Assert.IsTrue(new CodeParser().Parse(code2).HasContentBase("Type1")); var code3 = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; using System.Xml; public partial class Type1 : IHasXmlNode {} "} }; // figures out that IHasXmlNode is an interface, not base // because of using + reference var asms = new[] {typeof(System.Xml.IHasXmlNode).Assembly}; Assert.IsFalse(new CodeParser().Parse(code3, asms).HasContentBase("Type1")); }
public void PropertyTypeImplementOnClass() { var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type1.Properties.Add(new PropertyModel { Alias = "prop1", ClrName = "Prop1", ClrType = typeof(string), }); var types = new[] { type1 }; var code = new Dictionary<string, string> { {"assembly", @" using Umbraco.ModelsBuilder; using Dang; namespace Dang { public partial class Type1 { [ImplementPropertyType(""prop1"")] public string Foo { get { return string.Empty; } } } } "} }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; Assert.AreEqual(1, btypes.Count); var btype1 = btypes[0]; Assert.AreEqual("Type1", btype1.ClrName); var sb = new StringBuilder(); builder.Generate(sb, btype1); var gen = sb.ToString(); Console.WriteLine(gen); Assert.Greater(0, gen.IndexOf("string Prop1")); }
public void PartialConstructorWithRename() { var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type1.Properties.Add(new PropertyModel { Alias = "prop1", ClrName = "Prop1", ClrType = typeof(string), }); var types = new[] { type1 }; var code = new Dictionary<string, string> { { "assembly", @" using Umbraco.ModelsBuilder; using Umbraco.Core.Models; [assembly:RenameContentType(""type1"", ""Type2"")] public partial class Type2 { public Type2(IPublishedContent content) : base(content) { // do our own stuff } } " } }; var parseResult = new CodeParser().Parse(code); Assert.IsFalse(parseResult.HasCtor("Type1")); Assert.IsTrue(parseResult.HasCtor("Type2")); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; var sb = new StringBuilder(); builder.Generate(sb, builder.GetModelsToGenerate().First()); var gen = sb.ToString(); var version = typeof(Builder).Assembly.GetName().Version; var expected = @"//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // // Umbraco.ModelsBuilder v" + version + @" // // Changes to this file will be lost if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Web; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; using Umbraco.ModelsBuilder; using Umbraco.ModelsBuilder.Umbraco; namespace Umbraco.Web.PublishedContentModels { // Content Type 1 with alias ""type1"" [PublishedContentModel(""type1"")] public partial class Type2 : PublishedContentModel { #pragma warning disable 0109 // new is redundant public new const string ModelTypeAlias = ""type1""; public new const PublishedItemType ModelItemType = PublishedItemType.Content; #pragma warning restore 0109 #pragma warning disable 0109 // new is redundant public new static PublishedContentType GetModelContentType() { return PublishedContentType.Get(ModelItemType, ModelTypeAlias); } #pragma warning restore 0109 public static PublishedPropertyType GetModelPropertyType<TValue>(Expression<Func<Type2, TValue>> selector) { return PublishedContentModelUtility.GetModelPropertyType(GetModelContentType(), selector); } [ImplementPropertyType(""prop1"")] public string Prop1 { get { return this.GetPropertyValue<string>(""prop1""); } } } } "; Console.WriteLine(gen); Assert.AreEqual(expected.ClearLf(), gen); }
/// <summary> /// Prepares generation by processing the result of code parsing. /// </summary> /// <remarks> /// Preparation includes figuring out from the existing code which models or properties should /// be ignored or renamed, etc. -- anything that comes from the attributes in the existing code. /// </remarks> private void Prepare() { // mark IsContentIgnored models that we discovered should be ignored // then propagate / ignore children of ignored contents // ignore content = don't generate a class for it, don't generate children foreach (var typeModel in _typeModels.Where(x => ParseResult.IsIgnored(x.Alias))) { typeModel.IsContentIgnored = true; } foreach (var typeModel in _typeModels.Where(x => !x.IsContentIgnored && x.EnumerateBaseTypes().Any(xx => xx.IsContentIgnored))) { typeModel.IsContentIgnored = true; } // handle model renames foreach (var typeModel in _typeModels.Where(x => ParseResult.IsContentRenamed(x.Alias))) { typeModel.ClrName = ParseResult.ContentClrName(typeModel.Alias); typeModel.IsRenamed = true; } // handle implement foreach (var typeModel in _typeModels.Where(x => ParseResult.HasContentImplement(x.Alias))) { typeModel.HasImplement = true; } // mark OmitBase models that we discovered already have a base class foreach (var typeModel in _typeModels.Where(x => ParseResult.HasContentBase(ParseResult.ContentClrName(x.Alias) ?? x.ClrName))) { typeModel.HasBase = true; } foreach (var typeModel in _typeModels) { // mark IsRemoved properties that we discovered should be ignored // ie is marked as ignored on type, or on any parent type var tm = typeModel; foreach (var property in typeModel.Properties .Where(property => tm.EnumerateBaseTypes(true).Any(x => ParseResult.IsPropertyIgnored(ParseResult.ContentClrName(x.Alias) ?? x.ClrName, property.Alias)))) { property.IsIgnored = true; } // handle property renames foreach (var property in typeModel.Properties) { property.ClrName = ParseResult.PropertyClrName(ParseResult.ContentClrName(typeModel.Alias) ?? typeModel.ClrName, property.Alias) ?? property.ClrName; } } // ensure we have no duplicates type names foreach (var xx in _typeModels.Where(x => !x.IsContentIgnored).GroupBy(x => x.ClrName).Where(x => x.Count() > 1)) { throw new InvalidOperationException($"Type name \"{xx.Key}\" is used" + $" for types with alias {string.Join(", ", xx.Select(x => x.ItemType + ":\"" + x.Alias + "\""))}. Names have to be unique." + " Consider using an attribute to assign different names to conflicting types."); } // ensure we have no duplicates property names foreach (var typeModel in _typeModels.Where(x => !x.IsContentIgnored)) { foreach (var xx in typeModel.Properties.Where(x => !x.IsIgnored).GroupBy(x => x.ClrName).Where(x => x.Count() > 1)) { throw new InvalidOperationException($"Property name \"{xx.Key}\" in type {typeModel.ItemType}:\"{typeModel.Alias}\"" + $" is used for properties with alias {string.Join(", ", xx.Select(x => "\"" + x.Alias + "\""))}. Names have to be unique." + " Consider using an attribute to assign different names to conflicting properties."); } } // ensure content & property type don't have identical name (csharp hates it) foreach (var typeModel in _typeModels.Where(x => !x.IsContentIgnored)) { foreach (var xx in typeModel.Properties.Where(x => !x.IsIgnored && x.ClrName == typeModel.ClrName)) { throw new InvalidOperationException($"The model class for content type with alias \"{typeModel.Alias}\" is named \"{xx.ClrName}\"." + $" CSharp does not support using the same name for the property with alias \"{xx.Alias}\"." + " Consider using an attribute to assign a different name to the property."); } } // ensure we have no collision between base types // NO: we may want to define a base class in a partial, on a model that has a parent // we are NOT checking that the defined base type does maintain the inheritance chain //foreach (var xx in _typeModels.Where(x => !x.IsContentIgnored).Where(x => x.BaseType != null && x.HasBase)) // throw new InvalidOperationException(string.Format("Type alias \"{0}\" has more than one parent class.", // xx.Alias)); // discover interfaces that need to be declared / implemented foreach (var typeModel in _typeModels) { // collect all the (non-removed) types implemented at parent level // ie the parent content types and the mixins content types, recursively var parentImplems = new List <TypeModel>(); if (typeModel.BaseType != null && !typeModel.BaseType.IsContentIgnored) { TypeModel.CollectImplems(parentImplems, typeModel.BaseType); } // interfaces we must declare we implement (initially empty) // ie this type's mixins, except those that have been removed, // and except those that are already declared at the parent level // in other words, DeclaringInterfaces is "local mixins" var declaring = typeModel.MixinTypes .Where(x => !x.IsContentIgnored) .Except(parentImplems); typeModel.DeclaringInterfaces.AddRange(declaring); // interfaces we must actually implement (initially empty) // if we declare we implement a mixin interface, we must actually implement // its properties, all recursively (ie if the mixin interface implements...) // so, starting with local mixins, we collect all the (non-removed) types above them var mixinImplems = new List <TypeModel>(); foreach (var i in typeModel.DeclaringInterfaces) { TypeModel.CollectImplems(mixinImplems, i); } // and then we remove from that list anything that is already declared at the parent level typeModel.ImplementingInterfaces.AddRange(mixinImplems.Except(parentImplems)); } // register using types foreach (var usingNamespace in ParseResult.UsingNamespaces) { if (!TypesUsing.Contains(usingNamespace)) { TypesUsing.Add(usingNamespace); } } // discover static mixin methods foreach (var typeModel in _typeModels) { typeModel.StaticMixinMethods.AddRange(ParseResult.StaticMixinMethods(typeModel.ClrName)); } // handle ctor foreach (var typeModel in _typeModels.Where(x => ParseResult.HasCtor(x.ClrName))) { typeModel.HasCtor = true; } }
public void GenerateMixinType() { // Umbraco returns nice, pascal-cased names Configuration.Config.StaticMixinGetters = true; // try the new way var type1 = new TypeModel { Id = 1, Alias = "type1", ClrName = "Type1", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, IsMixin = true, }; type1.Properties.Add(new PropertyModel { Alias = "prop1", ClrName = "Prop1", ClrType = typeof(string), }); var type2 = new TypeModel { Id = 2, Alias = "type2", ClrName = "Type2", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, }; type2.MixinTypes.Add(type1); type2.Properties.Add(new PropertyModel { Alias = "prop2", ClrName = "Prop2", ClrType = typeof(int), }); var types = new[] { type1, type2 }; var code = new Dictionary<string, string> { }; var parseResult = new CodeParser().Parse(code); var builder = new TextBuilder(types, parseResult); var btypes = builder.TypeModels; var sb = new StringBuilder(); foreach (var model in builder.GetModelsToGenerate()) builder.Generate(sb, model); var gen = sb.ToString(); var version = typeof(Builder).Assembly.GetName().Version; var expected = @"//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // // Umbraco.ModelsBuilder v" + version + @" // // Changes to this file will be lost if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ "; Console.WriteLine(gen); return; //Assert.AreEqual(expected.ClearLf(), gen); }
/// <summary> /// Outputs a generated model to a code namespace. /// </summary> /// <param name="ns">The code namespace.</param> /// <param name="typeModel">The model to generate.</param> public void Generate(CodeNamespace ns, TypeModel typeModel) { // what about USING? // what about references? if (typeModel.IsMixin) { var i = new CodeTypeDeclaration("I" + typeModel.ClrName) { IsInterface = true, IsPartial = true, Attributes = MemberAttributes.Public }; i.BaseTypes.Add(typeModel.BaseType == null ? "IPublishedContent" : "I" + typeModel.BaseType.ClrName); foreach (var mixinType in typeModel.DeclaringInterfaces) i.BaseTypes.Add(mixinType.ClrName); i.Comments.Add(new CodeCommentStatement( string.Format("Mixin content Type {0} with alias \"{1}\"", typeModel.Id, typeModel.Alias))); foreach (var propertyModel in typeModel.Properties) { var p = new CodeMemberProperty(); p.Name = propertyModel.ClrName; p.Type = new CodeTypeReference(propertyModel.ClrType); p.Attributes = MemberAttributes.Public; p.HasGet = true; p.HasSet = false; i.Members.Add(p); } } var c = new CodeTypeDeclaration(typeModel.ClrName) { IsClass = true, IsPartial = true, Attributes = MemberAttributes.Public }; c.BaseTypes.Add(typeModel.BaseType == null ? "PublishedContentModel" : typeModel.BaseType.ClrName); // if it's a missing it implements its own interface if (typeModel.IsMixin) c.BaseTypes.Add("I" + typeModel.ClrName); // write the mixins, if any, as interfaces // only if not a mixin because otherwise the interface already has them if (typeModel.IsMixin == false) foreach (var mixinType in typeModel.DeclaringInterfaces) c.BaseTypes.Add("I" + mixinType.ClrName); foreach (var mixin in typeModel.MixinTypes) c.BaseTypes.Add("I" + mixin.ClrName); c.Comments.Add(new CodeCommentStatement( string.Format("Content Type {0} with alias \"{1}\"", typeModel.Id, typeModel.Alias))); foreach (var propertyModel in typeModel.Properties) { var p = new CodeMemberProperty(); p.Name = propertyModel.ClrName; p.Type = new CodeTypeReference(propertyModel.ClrType); p.Attributes = MemberAttributes.Public; p.HasGet = true; p.HasSet = false; p.GetStatements.Add(new CodeMethodReturnStatement( // return new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), // this "GetPropertyValue", // .GetPropertyValue new[] // <T> { new CodeTypeReference(propertyModel.ClrType) }), new CodeExpression[] // ("alias") { new CodePrimitiveExpression(propertyModel.Alias) }))); c.Members.Add(p); } }
private void WriteContentType(StringBuilder sb, TypeModel type) { string sep; if (type.IsMixin) { // write the interface declaration sb.AppendFormat("\t// Mixin content Type {0} with alias \"{1}\"\n", type.Id, type.Alias); if (!string.IsNullOrWhiteSpace(type.Name)) sb.AppendFormat("\t/// <summary>{0}</summary>\n", XmlCommentString(type.Name)); sb.AppendFormat("\tpublic partial interface I{0}", type.ClrName); var implements = type.BaseType == null || type.BaseType.IsContentIgnored ? (type.HasBase ? null : "PublishedContent") : type.BaseType.ClrName; if (implements != null) sb.AppendFormat(" : I{0}", implements); // write the mixins sep = implements == null ? ":" : ","; foreach (var mixinType in type.DeclaringInterfaces.OrderBy(x => x.ClrName)) { sb.AppendFormat("{0} I{1}", sep, mixinType.ClrName); sep = ","; } sb.Append("\n\t{\n"); // write the properties - only the local (non-ignored) ones, we're an interface var more = false; foreach (var prop in type.Properties.Where(x => !x.IsIgnored).OrderBy(x => x.ClrName)) { if (more) sb.Append("\n"); more = true; WriteInterfaceProperty(sb, prop); } sb.Append("\t}\n\n"); } // write the class declaration if (type.IsRenamed) sb.AppendFormat("\t// Content Type {0} with alias \"{1}\"\n", type.Id, type.Alias); if (!string.IsNullOrWhiteSpace(type.Name)) sb.AppendFormat("\t/// <summary>{0}</summary>\n", XmlCommentString(type.Name)); // cannot do it now. see note in ImplementContentTypeAttribute //if (!type.HasImplement) // sb.AppendFormat("\t[ImplementContentType(\"{0}\")]\n", type.Alias); sb.AppendFormat("\t[PublishedContentModel(\"{0}\")]\n", type.Alias); sb.AppendFormat("\tpublic partial class {0}", type.ClrName); var inherits = type.HasBase ? null // has its own base already : (type.BaseType == null || type.BaseType.IsContentIgnored ? GetModelsBaseClassName() : type.BaseType.ClrName); if (inherits != null) sb.AppendFormat(" : {0}", inherits); sep = inherits == null ? ":" : ","; if (type.IsMixin) { // if it's a mixin it implements its own interface sb.AppendFormat("{0} I{1}", sep, type.ClrName); } else { // write the mixins, if any, as interfaces // only if not a mixin because otherwise the interface already has them already foreach (var mixinType in type.DeclaringInterfaces.OrderBy(x => x.ClrName)) { sb.AppendFormat("{0} I{1}", sep, mixinType.ClrName); sep = ","; } } // begin class body sb.Append("\n\t{\n"); // write the constants // as 'new' since parent has its own - or maybe not - disable warning sb.Append("#pragma warning disable 0109 // new is redundant\n"); sb.AppendFormat("\t\tpublic new const string ModelTypeAlias = \"{0}\";\n", type.Alias); sb.AppendFormat("\t\tpublic new const PublishedItemType ModelItemType = PublishedItemType.{0};\n", type.ItemType); sb.Append("#pragma warning restore 0109\n\n"); // write the ctor if (!type.HasCtor) sb.AppendFormat("\t\tpublic {0}(IPublishedContent content)\n\t\t\t: base(content)\n\t\t{{ }}\n\n", type.ClrName); // write the static methods // as 'new' since parent has its own - or maybe not - disable warning sb.Append("#pragma warning disable 0109 // new is redundant\n"); sb.Append("\t\tpublic new static PublishedContentType GetModelContentType()\n"); sb.Append("\t\t{\n\t\t\treturn PublishedContentType.Get(ModelItemType, ModelTypeAlias);\n\t\t}\n"); sb.Append("#pragma warning restore 0109\n\n"); sb.AppendFormat("\t\tpublic static PublishedPropertyType GetModelPropertyType<TValue>(Expression<Func<{0}, TValue>> selector)\n", type.ClrName); sb.Append("\t\t{\n\t\t\treturn PublishedContentModelUtility.GetModelPropertyType(GetModelContentType(), selector);\n\t\t}\n"); // write the properties WriteContentTypeProperties(sb, type); // close the class declaration sb.Append("\t}\n"); }