Skip to content

Commit 4779ad1

Browse files
committed
Separate TypeModel.cs types into separate files
1 parent 61ff93e commit 4779ad1

11 files changed

Lines changed: 1420 additions & 1357 deletions
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
using System;
2+
using System.CodeDom;
3+
using System.CodeDom.Compiler;
4+
using System.Collections.Generic;
5+
using System.ComponentModel;
6+
using System.Diagnostics;
7+
using System.Linq;
8+
using System.Xml.Schema;
9+
using System.Xml.Serialization;
10+
11+
namespace XmlSchemaClassGenerator;
12+
13+
public class ClassModel(GeneratorConfiguration configuration) : ReferenceTypeModel(configuration)
14+
{
15+
public override bool IsRedefined => DerivedTypes.Exists(d => d.XmlSchemaType?.Parent is XmlSchemaRedefine);
16+
public bool IsAbstract { get; set; }
17+
public bool IsMixed { get; set; }
18+
public bool IsSubstitution { get; set; }
19+
public TypeModel BaseClass { get; set; }
20+
public List<ClassModel> DerivedTypes { get; set; } = [];
21+
public override bool IsSubtype => BaseClass != null;
22+
23+
public IEnumerable<ClassModel> AllBaseClasses
24+
{
25+
get
26+
{
27+
var baseClass = BaseClass as ClassModel;
28+
while (baseClass != null)
29+
{
30+
yield return baseClass;
31+
baseClass = baseClass.BaseClass as ClassModel;
32+
}
33+
}
34+
}
35+
36+
public IEnumerable<TypeModel> AllBaseTypes
37+
{
38+
get
39+
{
40+
var baseType = BaseClass;
41+
while (baseType != null)
42+
{
43+
yield return baseType;
44+
baseType = (baseType as ClassModel)?.BaseClass;
45+
}
46+
}
47+
}
48+
49+
public override CodeTypeDeclaration Generate()
50+
{
51+
var classDeclaration = base.Generate();
52+
53+
GenerateSerializableAttribute(classDeclaration);
54+
GenerateTypeAttribute(classDeclaration);
55+
56+
classDeclaration.IsClass = true;
57+
classDeclaration.IsPartial = true;
58+
if (Configuration.AssemblyVisible)
59+
classDeclaration.TypeAttributes = (classDeclaration.TypeAttributes & ~System.Reflection.TypeAttributes.VisibilityMask) | System.Reflection.TypeAttributes.NestedAssembly;
60+
61+
if (IsAbstract)
62+
classDeclaration.TypeAttributes |= System.Reflection.TypeAttributes.Abstract;
63+
64+
if (Configuration.EnableDataBinding && BaseClass is not ClassModel)
65+
{
66+
var propertyChangedEvent = new CodeMemberEvent()
67+
{
68+
Name = nameof(INotifyPropertyChanged.PropertyChanged),
69+
Type = TypeRef<PropertyChangedEventHandler>(),
70+
Attributes = MemberAttributes.Public,
71+
};
72+
classDeclaration.Members.Add(propertyChangedEvent);
73+
74+
SimpleModel type = new(Configuration) { ValueType = typeof(PropertyChangedEventHandler) };
75+
var propertyChangedModel = new PropertyModel(Configuration, propertyChangedEvent.Name, type, this);
76+
77+
Configuration.MemberVisitor(propertyChangedEvent, propertyChangedModel);
78+
79+
var param = new CodeParameterDeclarationExpression(typeof(string), "propertyName = null");
80+
param.CustomAttributes.Add(new(TypeRef<System.Runtime.CompilerServices.CallerMemberNameAttribute>()));
81+
var threadSafeDelegateInvokeExpression = new CodeSnippetExpression($"{propertyChangedEvent.Name}?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName))");
82+
var onPropChangedMethod = new CodeMemberMethod
83+
{
84+
Name = OnPropertyChanged,
85+
Attributes = MemberAttributes.Family,
86+
Parameters = { param },
87+
Statements = { threadSafeDelegateInvokeExpression }
88+
};
89+
90+
classDeclaration.Members.Add(onPropChangedMethod);
91+
}
92+
93+
if (BaseClass != null)
94+
{
95+
if (BaseClass is ClassModel)
96+
{
97+
classDeclaration.BaseTypes.Add(BaseClass.GetReferenceFor(Namespace));
98+
}
99+
else if (!string.IsNullOrEmpty(Configuration.TextValuePropertyName))
100+
{
101+
var textName = Configuration.TextValuePropertyName;
102+
var enableDataBinding = Configuration.EnableDataBinding;
103+
var typeReference = BaseClass.GetReferenceFor(Namespace);
104+
105+
CodeMemberField backingFieldMember = null;
106+
if (enableDataBinding)
107+
{
108+
backingFieldMember = new CodeMemberField(typeReference, textName.ToBackingField(Configuration.PrivateMemberPrefix))
109+
{
110+
Attributes = MemberAttributes.Private
111+
};
112+
classDeclaration.Members.Add(backingFieldMember);
113+
}
114+
115+
CodeMemberField text = new(typeReference, textName + PropertyModel.GetAccessors(backingFieldMember, enableDataBinding, BaseClass.GetPropertyValueTypeCode()))
116+
{
117+
Attributes = MemberAttributes.Public,
118+
};
119+
120+
var docs = new List<DocumentationModel> {
121+
new() { Language = English, Text = "Gets or sets the text value." },
122+
new() { Language = German, Text = "Ruft den Text ab oder legt diesen fest." }
123+
};
124+
125+
var attribute = AttributeDecl<XmlTextAttribute>();
126+
127+
if (BaseClass is SimpleModel simpleModel)
128+
{
129+
docs.AddRange(simpleModel.Restrictions.Select(r => new DocumentationModel { Language = English, Text = r.Description }));
130+
text.CustomAttributes.AddRange(simpleModel.GetRestrictionAttributes().ToArray());
131+
132+
if (BaseClass.GetQualifiedName() is { Namespace: XmlSchema.Namespace, Name: var name } && (simpleModel.XmlSchemaType.Datatype.IsDataTypeAttributeAllowed(Configuration) ?? simpleModel.UseDataTypeAttribute))
133+
attribute.Arguments.Add(new CodeAttributeArgument(nameof(XmlTextAttribute.DataType), new CodePrimitiveExpression(name)));
134+
}
135+
136+
text.Comments.AddRange(GetComments(docs).ToArray());
137+
138+
text.CustomAttributes.Add(attribute);
139+
classDeclaration.Members.Add(text);
140+
141+
var valuePropertyModel = new PropertyModel(Configuration, textName, BaseClass, this);
142+
143+
Configuration.MemberVisitor(text, valuePropertyModel);
144+
}
145+
}
146+
147+
if (Configuration.EnableDataBinding)
148+
{
149+
classDeclaration.BaseTypes.Add(TypeRef<INotifyPropertyChanged>());
150+
}
151+
152+
if (Configuration.EntityFramework && BaseClass is not ClassModel)
153+
{
154+
// generate key
155+
var keyProperty = Properties.Find(p => string.Equals(p.Name, "id", StringComparison.InvariantCultureIgnoreCase))
156+
?? Properties.Find(p => p.Name.ToLowerInvariant() == Name.ToLowerInvariant() + "id");
157+
158+
if (keyProperty == null)
159+
{
160+
keyProperty = new PropertyModel(Configuration, "Id", new SimpleModel(Configuration) { ValueType = typeof(long) }, this)
161+
{
162+
Documentation = {
163+
new() { Language = English, Text = "Gets or sets a value uniquely identifying this entity." },
164+
new() { Language = German, Text = "Ruft einen Wert ab, der diese Entität eindeutig identifiziert, oder legt diesen fest." }
165+
},
166+
IsRequired = true
167+
};
168+
Properties.Insert(0, keyProperty);
169+
}
170+
171+
keyProperty.IsKey = true;
172+
}
173+
174+
var properties = Properties.GroupBy(x => x.Name).SelectMany(g => g.Select((p, i) => (Property: p, Index: i)).ToList());
175+
foreach (var (Property, Index) in properties)
176+
{
177+
if (Index > 0)
178+
{
179+
Property.Name += $"_{Index + 1}";
180+
181+
if (properties.Any(q => Property.XmlSchemaName == q.Property.XmlSchemaName && q.Index < Index))
182+
continue;
183+
}
184+
185+
Property.AddMembersTo(classDeclaration, Configuration.EnableDataBinding);
186+
}
187+
188+
if (IsMixed && (BaseClass == null || (BaseClass is ClassModel && !AllBaseClasses.Any(b => b.IsMixed))))
189+
{
190+
var propName = "Text";
191+
var propertyIndex = 1;
192+
193+
// To not collide with any existing members
194+
while (Properties.Exists(x => x.Name.Equals(propName, StringComparison.Ordinal)) || propName.Equals(classDeclaration.Name, StringComparison.Ordinal))
195+
{
196+
propName = $"Text_{propertyIndex}";
197+
propertyIndex++;
198+
}
199+
// hack to generate automatic property
200+
var text = new CodeMemberField(typeof(string[]), propName + PropertyModel.GetAccessors()) { Attributes = MemberAttributes.Public };
201+
text.CustomAttributes.Add(AttributeDecl<XmlTextAttribute>());
202+
classDeclaration.Members.Add(text);
203+
204+
var textPropertyModel = new PropertyModel(Configuration, propName, new SimpleModel(Configuration) { ValueType = typeof(string) }, this);
205+
206+
Configuration.MemberVisitor(text, textPropertyModel);
207+
}
208+
209+
var customAttributes = classDeclaration.CustomAttributes;
210+
211+
if (Configuration.GenerateDebuggerStepThroughAttribute)
212+
customAttributes.Add(AttributeDecl<DebuggerStepThroughAttribute>());
213+
214+
if (Configuration.GenerateDesignerCategoryAttribute)
215+
customAttributes.Add(AttributeDecl<DesignerCategoryAttribute>(new CodeAttributeArgument(new CodePrimitiveExpression("code"))));
216+
217+
if (RootElementName != null)
218+
{
219+
var rootAttribute = AttributeDecl<XmlRootAttribute>(
220+
new(new CodePrimitiveExpression(RootElementName.Name)),
221+
new(nameof(XmlRootAttribute.Namespace), new CodePrimitiveExpression(RootElementName.Namespace)));
222+
customAttributes.Add(rootAttribute);
223+
}
224+
225+
if (!Configuration.OmitXmlIncludeAttribute)
226+
{
227+
var derivedTypes = GetAllDerivedTypes();
228+
foreach (var derivedType in derivedTypes.OrderBy(t => t.Name))
229+
customAttributes.Add(AttributeDecl<XmlIncludeAttribute>(new CodeAttributeArgument(new CodeTypeOfExpression(derivedType.GetReferenceFor(Namespace)))));
230+
}
231+
232+
classDeclaration.BaseTypes.AddRange(Interfaces.Select(i => i.GetReferenceFor(Namespace)).ToArray());
233+
234+
Configuration.TypeVisitor(classDeclaration, this);
235+
return classDeclaration;
236+
}
237+
238+
public List<ClassModel> GetAllDerivedTypes()
239+
{
240+
var allDerivedTypes = new List<ClassModel>(DerivedTypes);
241+
242+
foreach (var derivedType in DerivedTypes)
243+
allDerivedTypes.AddRange(derivedType.GetAllDerivedTypes());
244+
245+
return allDerivedTypes;
246+
}
247+
248+
public override CodeExpression GetDefaultValueFor(string defaultString, bool attribute)
249+
{
250+
var rootClass = AllBaseTypes.LastOrDefault();
251+
252+
if (rootClass is SimpleModel || rootClass is EnumModel)
253+
{
254+
string reference, val;
255+
256+
using (var writer = new System.IO.StringWriter())
257+
{
258+
CSharpProvider.GenerateCodeFromExpression(rootClass.GetDefaultValueFor(defaultString, attribute), writer, new CodeGeneratorOptions());
259+
val = writer.ToString();
260+
}
261+
262+
using (var writer = new System.IO.StringWriter())
263+
{
264+
CSharpProvider.GenerateCodeFromExpression(new CodeTypeReferenceExpression(GetReferenceFor(referencingNamespace: null)), writer, new CodeGeneratorOptions());
265+
reference = writer.ToString();
266+
}
267+
268+
return new CodeSnippetExpression($"new {reference} {{ {Configuration.TextValuePropertyName} = {val} }};");
269+
}
270+
271+
return base.GetDefaultValueFor(defaultString, attribute);
272+
}
273+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace XmlSchemaClassGenerator;
2+
3+
public class DocumentationModel
4+
{
5+
public string Language { get; set; }
6+
public string Text { get; set; }
7+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System.CodeDom;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Xml.Serialization;
5+
6+
namespace XmlSchemaClassGenerator;
7+
8+
public class EnumModel(GeneratorConfiguration configuration) : TypeModel(configuration)
9+
{
10+
public List<EnumValueModel> Values { get; set; } = [];
11+
12+
public override CodeTypeDeclaration Generate()
13+
{
14+
var enumDeclaration = base.Generate();
15+
16+
GenerateSerializableAttribute(enumDeclaration);
17+
GenerateTypeAttribute(enumDeclaration);
18+
19+
enumDeclaration.IsEnum = true;
20+
if (Configuration.AssemblyVisible)
21+
{
22+
enumDeclaration.TypeAttributes = (enumDeclaration.TypeAttributes & ~System.Reflection.TypeAttributes.VisibilityMask) | System.Reflection.TypeAttributes.NestedAssembly;
23+
}
24+
25+
foreach (var val in Values)
26+
{
27+
var member = new CodeMemberField { Name = val.Name };
28+
var docs = new List<DocumentationModel>(val.Documentation);
29+
30+
AddDescription(member.CustomAttributes, docs);
31+
32+
if (val.Name != val.Value) // illegal identifier chars in value
33+
{
34+
var enumAttribute = AttributeDecl<XmlEnumAttribute>(new CodeAttributeArgument(new CodePrimitiveExpression(val.Value)));
35+
member.CustomAttributes.Add(enumAttribute);
36+
}
37+
38+
if (val.IsDeprecated)
39+
{
40+
// From .NET 3.5 XmlSerializer doesn't serialize objects with [Obsolete] >(
41+
42+
DocumentationModel obsolete = new() { Language = English, Text = "[Obsolete]" };
43+
docs.Add(obsolete);
44+
}
45+
46+
member.Comments.AddRange(GetComments(docs).ToArray());
47+
48+
enumDeclaration.Members.Add(member);
49+
}
50+
51+
if (RootElementName != null)
52+
{
53+
var rootAttribute = AttributeDecl<XmlRootAttribute>(
54+
new(new CodePrimitiveExpression(RootElementName.Name)),
55+
new(nameof(XmlRootAttribute.Namespace), new CodePrimitiveExpression(RootElementName.Namespace)));
56+
enumDeclaration.CustomAttributes.Add(rootAttribute);
57+
}
58+
Configuration.TypeVisitor(enumDeclaration, this);
59+
return enumDeclaration;
60+
}
61+
62+
public override CodeExpression GetDefaultValueFor(string defaultString, bool attribute)
63+
{
64+
return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(GetReferenceFor(referencingNamespace: null)),
65+
Values.First(v => v.Value == defaultString).Name);
66+
}
67+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Collections.Generic;
2+
3+
namespace XmlSchemaClassGenerator;
4+
5+
public class EnumValueModel
6+
{
7+
public string Name { get; set; }
8+
public string Value { get; set; }
9+
public bool IsDeprecated { get; set; }
10+
public List<DocumentationModel> Documentation { get; } = [];
11+
}

0 commit comments

Comments
 (0)