Migrate XsdGen
Rob Mensching committed
Aug 15, 2017 at 00:36 UTC
0358780c77469974dd8bc87840364e790ccecc29
14 files changed
+2721
WixBuildTools.sln
new
+41
@@ -0,0 +1,41 @@
1
+Microsoft Visual Studio Solution File, Format Version 12.00
2
+# Visual Studio 15
3
+VisualStudioVersion = 15.0.26730.3
4
+MinimumVisualStudioVersion = 15.0.26124.0
5
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{00E50075-E896-42D1-AC30-2D2E7D129FB9}"
6
+EndProject
7
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.XsdGen", "src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj", "{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}"
8
+EndProject
9
+Global
10
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
11
+ Debug|Any CPU = Debug|Any CPU
12
+ Debug|x64 = Debug|x64
13
+ Debug|x86 = Debug|x86
14
+ Release|Any CPU = Release|Any CPU
15
+ Release|x64 = Release|x64
16
+ Release|x86 = Release|x86
17
+ EndGlobalSection
18
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
19
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
21
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.ActiveCfg = Debug|Any CPU
22
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.Build.0 = Debug|Any CPU
23
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.ActiveCfg = Debug|Any CPU
24
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.Build.0 = Debug|Any CPU
25
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
26
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.Build.0 = Release|Any CPU
27
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.ActiveCfg = Release|Any CPU
28
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.Build.0 = Release|Any CPU
29
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.ActiveCfg = Release|Any CPU
30
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.Build.0 = Release|Any CPU
31
+ EndGlobalSection
32
+ GlobalSection(SolutionProperties) = preSolution
33
+ HideSolutionNode = FALSE
34
+ EndGlobalSection
35
+ GlobalSection(NestedProjects) = preSolution
36
+ {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6} = {00E50075-E896-42D1-AC30-2D2E7D129FB9}
37
+ EndGlobalSection
38
+ GlobalSection(ExtensibilityGlobals) = postSolution
39
+ SolutionGuid = {83E9E075-B440-471A-9C37-9D84BA0AE3E0}
40
+ EndGlobalSection
41
+EndGlobal
appveyor.yml
new
+24
@@ -0,0 +1,24 @@
1
+image: Visual Studio 2017 Preview
2
+
3
+version: 0.0.0.{build}
4
+configuration: Release
5
+
6
+environment:
7
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
8
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
9
+ NUGET_XMLDOC_MODE: skip
10
+
11
+build_script:
12
+- dotnet pack
13
+
14
+pull_requests:
15
+ do_not_increment_build_number: true
16
+
17
+nuget:
18
+ disable_publish_on_pr: true
19
+
20
+skip_tags: true
21
+
22
+artifacts:
23
+- path: build\Release\**\*.nupkg
24
+ name: nuget
src/Directory.Build.props
new
+12
@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project>
3
+ <PropertyGroup>
4
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5
+ <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
6
+ <OutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</OutputPath>
7
+
8
+ <Authors>Rob Mensching, Bob Arnson</Authors>
9
+ <Company>WiX Toolset</Company>
10
+ <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
11
+ </PropertyGroup>
12
+</Project>
src/WixBuildTools.XsdGen/AssemblyInfo.cs
new
+9
@@ -0,0 +1,9 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+using System;
4
+using System.Reflection;
5
+using System.Runtime.InteropServices;
6
+
7
+[assembly: AssemblyCulture("")]
8
+[assembly: CLSCompliant(true)]
9
+[assembly: ComVisible(false)]
src/WixBuildTools.XsdGen/CodeDomInterfaces.cs
new
+96
@@ -0,0 +1,96 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Serialize
4
+{
5
+ using System;
6
+ using System.Collections;
7
+ using System.Xml;
8
+
9
+ /// <summary>
10
+ /// Interface for generated schema elements.
11
+ /// </summary>
12
+ public interface ISchemaElement
13
+ {
14
+ /// <summary>
15
+ /// Gets and sets the parent of this element. May be null.
16
+ /// </summary>
17
+ /// <value>An ISchemaElement that has this element as a child.</value>
18
+ ISchemaElement ParentElement
19
+ {
20
+ get;
21
+ set;
22
+ }
23
+
24
+ /// <summary>
25
+ /// Outputs xml representing this element, including the associated attributes
26
+ /// and any nested elements.
27
+ /// </summary>
28
+ /// <param name="writer">XmlTextWriter to be used when outputting the element.</param>
29
+ void OutputXml(XmlWriter writer);
30
+ }
31
+
32
+ /// <summary>
33
+ /// Interface for generated schema elements. Implemented by elements that have child
34
+ /// elements.
35
+ /// </summary>
36
+ public interface IParentElement
37
+ {
38
+ /// <summary>
39
+ /// Gets an enumerable collection of the children of this element.
40
+ /// </summary>
41
+ /// <value>An enumerable collection of the children of this element.</value>
42
+ IEnumerable Children
43
+ {
44
+ get;
45
+ }
46
+
47
+ /// <summary>
48
+ /// Gets an enumerable collection of the children of this element, filtered
49
+ /// by the passed in type.
50
+ /// </summary>
51
+ /// <param name="childType">The type of children to retrieve.</param>
52
+ IEnumerable this[Type childType]
53
+ {
54
+ get;
55
+ }
56
+
57
+ /// <summary>
58
+ /// Adds a child to this element.
59
+ /// </summary>
60
+ /// <param name="child">Child to add.</param>
61
+ void AddChild(ISchemaElement child);
62
+
63
+ /// <summary>
64
+ /// Removes a child from this element.
65
+ /// </summary>
66
+ /// <param name="child">Child to remove.</param>
67
+ void RemoveChild(ISchemaElement child);
68
+ }
69
+
70
+ /// <summary>
71
+ /// Interface for generated schema elements. Implemented by classes with attributes.
72
+ /// </summary>
73
+ public interface ISetAttributes
74
+ {
75
+ /// <summary>
76
+ /// Sets the attribute with the given name to the given value. The value here is
77
+ /// a string, and is converted to the strongly-typed version inside this method.
78
+ /// </summary>
79
+ /// <param name="name">The name of the attribute to set.</param>
80
+ /// <param name="value">The value to assign to the attribute.</param>
81
+ void SetAttribute(string name, string value);
82
+ }
83
+
84
+ /// <summary>
85
+ /// Interface for generated schema elements. Implemented by classes with children.
86
+ /// </summary>
87
+ public interface ICreateChildren
88
+ {
89
+ /// <summary>
90
+ /// Creates an instance of the child with the passed in name.
91
+ /// </summary>
92
+ /// <param name="childName">String matching the element name of the child when represented in XML.</param>
93
+ /// <returns>An instance of that child.</returns>
94
+ ISchemaElement CreateChild(string childName);
95
+ }
96
+}
src/WixBuildTools.XsdGen/CodeDomReader.cs
new
+159
@@ -0,0 +1,159 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Serialize
4
+{
5
+ using System;
6
+ using System.Collections;
7
+ using System.Reflection;
8
+ using System.Xml;
9
+
10
+ /// <summary>
11
+ /// Class used for reading XML files in to the CodeDom.
12
+ /// </summary>
13
+ public class CodeDomReader
14
+ {
15
+ private Assembly[] assemblies;
16
+
17
+ /// <summary>
18
+ /// Creates a new CodeDomReader, using the current assembly.
19
+ /// </summary>
20
+ public CodeDomReader()
21
+ {
22
+ this.assemblies = new Assembly[] { Assembly.GetExecutingAssembly() };
23
+ }
24
+
25
+ /// <summary>
26
+ /// Creates a new CodeDomReader, and takes in a list of assemblies in which to
27
+ /// look for elements.
28
+ /// </summary>
29
+ /// <param name="assemblies">Assemblies in which to look for types that correspond
30
+ /// to elements.</param>
31
+ public CodeDomReader(Assembly[] assemblies)
32
+ {
33
+ this.assemblies = assemblies;
34
+ }
35
+
36
+ /// <summary>
37
+ /// Loads an XML file into a strongly-typed code dom.
38
+ /// </summary>
39
+ /// <param name="filePath">File to load into the code dom.</param>
40
+ /// <returns>The strongly-typed object at the root of the tree.</returns>
41
+ public ISchemaElement Load(string filePath)
42
+ {
43
+ XmlDocument document = new XmlDocument();
44
+ document.Load(filePath);
45
+ ISchemaElement schemaElement = null;
46
+
47
+ foreach (XmlNode node in document.ChildNodes)
48
+ {
49
+ XmlElement element = node as XmlElement;
50
+ if (element != null)
51
+ {
52
+ if (schemaElement != null)
53
+ {
54
+ throw new InvalidOperationException("Multiple root elements found in file.");
55
+ }
56
+
57
+ schemaElement = this.CreateObjectFromElement(element);
58
+ this.ParseObjectFromElement(schemaElement, element);
59
+ }
60
+ }
61
+ return schemaElement;
62
+ }
63
+
64
+ /// <summary>
65
+ /// Parses an ISchemaElement from the XmlElement.
66
+ /// </summary>
67
+ /// <param name="schemaElement">ISchemaElement to fill in.</param>
68
+ /// <param name="element">XmlElement to parse from.</param>
69
+ private void ParseObjectFromElement(ISchemaElement schemaElement, XmlElement element)
70
+ {
71
+ foreach (XmlAttribute attribute in element.Attributes)
72
+ {
73
+ this.SetAttributeOnObject(schemaElement, attribute.LocalName, attribute.Value);
74
+ }
75
+
76
+ foreach (XmlNode node in element.ChildNodes)
77
+ {
78
+ XmlElement childElement = node as XmlElement;
79
+ if (childElement != null)
80
+ {
81
+ ISchemaElement childSchemaElement = null;
82
+ ICreateChildren createChildren = schemaElement as ICreateChildren;
83
+ if (createChildren == null)
84
+ {
85
+ throw new InvalidOperationException("ISchemaElement with name " + element.LocalName + " does not implement ICreateChildren.");
86
+ }
87
+ else
88
+ {
89
+ childSchemaElement = createChildren.CreateChild(childElement.LocalName);
90
+ }
91
+
92
+ if (childSchemaElement == null)
93
+ {
94
+ childSchemaElement = this.CreateObjectFromElement(childElement);
95
+ if (childSchemaElement == null)
96
+ {
97
+ throw new InvalidOperationException("XmlElement with name " + childElement.LocalName + " does not have a corresponding ISchemaElement.");
98
+ }
99
+ }
100
+
101
+ this.ParseObjectFromElement(childSchemaElement, childElement);
102
+ IParentElement parentElement = (IParentElement)schemaElement;
103
+ parentElement.AddChild(childSchemaElement);
104
+ }
105
+ else
106
+ {
107
+ XmlText childText = node as XmlText;
108
+ if (childText != null)
109
+ {
110
+ this.SetAttributeOnObject(schemaElement, "Content", childText.Value);
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ /// <summary>
117
+ /// Sets an attribute on an ISchemaElement.
118
+ /// </summary>
119
+ /// <param name="schemaElement">Schema element to set attribute on.</param>
120
+ /// <param name="name">Name of the attribute to set.</param>
121
+ /// <param name="value">Value to set on the attribute.</param>
122
+ private void SetAttributeOnObject(ISchemaElement schemaElement, string name, string value)
123
+ {
124
+ ISetAttributes setAttributes = schemaElement as ISetAttributes;
125
+ if (setAttributes == null)
126
+ {
127
+ throw new InvalidOperationException("ISchemaElement with name "
128
+ + schemaElement.GetType().FullName.ToString()
129
+ + " does not implement ISetAttributes.");
130
+ }
131
+ else
132
+ {
133
+ setAttributes.SetAttribute(name, value);
134
+ }
135
+ }
136
+
137
+ /// <summary>
138
+ /// Creates an object from an XML element by digging through the assembly list.
139
+ /// </summary>
140
+ /// <param name="element">XML Element to create an ISchemaElement from.</param>
141
+ /// <returns>A constructed ISchemaElement.</returns>
142
+ private ISchemaElement CreateObjectFromElement(XmlElement element)
143
+ {
144
+ ISchemaElement schemaElement = null;
145
+ foreach (Assembly assembly in this.assemblies)
146
+ {
147
+ foreach (Type type in assembly.GetTypes())
148
+ {
149
+ if (type.FullName.EndsWith(element.LocalName)
150
+ && typeof(ISchemaElement).IsAssignableFrom(type))
151
+ {
152
+ schemaElement = (ISchemaElement)Activator.CreateInstance(type);
153
+ }
154
+ }
155
+ }
156
+ return schemaElement;
157
+ }
158
+ }
159
+}
src/WixBuildTools.XsdGen/ElementCollection.cs
new
+642
@@ -0,0 +1,642 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Serialize
4
+{
5
+ using System;
6
+ using System.Collections;
7
+ using System.Globalization;
8
+
9
+ /// <summary>
10
+ /// Collection used in the CodeDOM for the children of a given element. Provides type-checking
11
+ /// on the allowed children to ensure that only allowed types are added.
12
+ /// </summary>
13
+ public class ElementCollection : ICollection, IEnumerable
14
+ {
15
+ private CollectionType collectionType;
16
+ private int minimum = 1;
17
+ private int maximum = 1;
18
+ private int totalContainedItems;
19
+ private int containersUsed;
20
+ private ArrayList items;
21
+
22
+ /// <summary>
23
+ /// Creates a new element collection.
24
+ /// </summary>
25
+ /// <param name="collectionType">Type of the collection to create.</param>
26
+ public ElementCollection(CollectionType collectionType)
27
+ {
28
+ this.collectionType = collectionType;
29
+ this.items = new ArrayList();
30
+ }
31
+
32
+ /// <summary>
33
+ /// Creates a new element collection.
34
+ /// </summary>
35
+ /// <param name="collectionType">Type of the collection to create.</param>
36
+ /// <param name="minimum">When used with a type 'Choice', specifies a minimum number of allowed children.</param>
37
+ /// <param name="maximum">When used with a type 'Choice', specifies a maximum number of allowed children.</param>
38
+ public ElementCollection(CollectionType collectionType, int minimum, int maximum) : this(collectionType)
39
+ {
40
+ this.minimum = minimum;
41
+ this.maximum = maximum;
42
+ }
43
+
44
+ /// <summary>
45
+ /// Enum representing types of XML collections.
46
+ /// </summary>
47
+ public enum CollectionType
48
+ {
49
+ /// <summary>
50
+ /// A choice type, corresponding to the XSD choice element.
51
+ /// </summary>
52
+ Choice,
53
+
54
+ /// <summary>
55
+ /// A sequence type, corresponding to the XSD sequence element.
56
+ /// </summary>
57
+ Sequence
58
+ }
59
+
60
+ /// <summary>
61
+ /// Gets the type of collection.
62
+ /// </summary>
63
+ /// <value>The type of collection.</value>
64
+ public CollectionType Type
65
+ {
66
+ get { return this.collectionType; }
67
+ }
68
+
69
+ /// <summary>
70
+ /// Gets the count of child elements in this collection (counts ISchemaElements, not nested collections).
71
+ /// </summary>
72
+ /// <value>The count of child elements in this collection (counts ISchemaElements, not nested collections).</value>
73
+ public int Count
74
+ {
75
+ get { return this.totalContainedItems; }
76
+ }
77
+
78
+ /// <summary>
79
+ /// Gets the flag specifying whether this collection is synchronized. Always returns false.
80
+ /// </summary>
81
+ /// <value>The flag specifying whether this collection is synchronized. Always returns false.</value>
82
+ public bool IsSynchronized
83
+ {
84
+ get { return false; }
85
+ }
86
+
87
+ /// <summary>
88
+ /// Gets an object external callers can synchronize on.
89
+ /// </summary>
90
+ /// <value>An object external callers can synchronize on.</value>
91
+ public object SyncRoot
92
+ {
93
+ get { return this; }
94
+ }
95
+
96
+ /// <summary>
97
+ /// Adds a child element to this collection.
98
+ /// </summary>
99
+ /// <param name="element">The element to add.</param>
100
+ /// <exception cref="ArgumentException">Thrown if the child is not of an allowed type.</exception>
101
+ public void AddElement(ISchemaElement element)
102
+ {
103
+ foreach (object obj in this.items)
104
+ {
105
+ bool containerUsed;
106
+
107
+ CollectionItem collectionItem = obj as CollectionItem;
108
+ if (collectionItem != null)
109
+ {
110
+ containerUsed = collectionItem.Elements.Count != 0;
111
+ if (collectionItem.ElementType.IsAssignableFrom(element.GetType()))
112
+ {
113
+ collectionItem.AddElement(element);
114
+
115
+ if (!containerUsed)
116
+ {
117
+ this.containersUsed++;
118
+ }
119
+
120
+ this.totalContainedItems++;
121
+ return;
122
+ }
123
+
124
+ continue;
125
+ }
126
+
127
+ ElementCollection collection = obj as ElementCollection;
128
+ if (collection != null)
129
+ {
130
+ containerUsed = collection.Count != 0;
131
+
132
+ try
133
+ {
134
+ collection.AddElement(element);
135
+
136
+ if (!containerUsed)
137
+ {
138
+ this.containersUsed++;
139
+ }
140
+
141
+ this.totalContainedItems++;
142
+ return;
143
+ }
144
+ catch (ArgumentException)
145
+ {
146
+ // Eat the exception and keep looking. We'll throw our own if we can't find its home.
147
+ }
148
+
149
+ continue;
150
+ }
151
+ }
152
+
153
+ throw new ArgumentException(String.Format(
154
+ CultureInfo.InvariantCulture,
155
+ "Element of type {0} is not valid for this collection.",
156
+ element.GetType().Name));
157
+ }
158
+
159
+ /// <summary>
160
+ /// Removes a child element from this collection.
161
+ /// </summary>
162
+ /// <param name="element">The element to remove.</param>
163
+ /// <exception cref="ArgumentException">Thrown if the element is not of an allowed type.</exception>
164
+ public void RemoveElement(ISchemaElement element)
165
+ {
166
+ foreach (object obj in this.items)
167
+ {
168
+ CollectionItem collectionItem = obj as CollectionItem;
169
+ if (collectionItem != null)
170
+ {
171
+ if (collectionItem.ElementType.IsAssignableFrom(element.GetType()))
172
+ {
173
+ if (collectionItem.Elements.Count == 0)
174
+ {
175
+ return;
176
+ }
177
+
178
+ collectionItem.RemoveElement(element);
179
+
180
+ if (collectionItem.Elements.Count == 0)
181
+ {
182
+ this.containersUsed--;
183
+ }
184
+
185
+ this.totalContainedItems--;
186
+ return;
187
+ }
188
+
189
+ continue;
190
+ }
191
+
192
+ ElementCollection collection = obj as ElementCollection;
193
+ if (collection != null)
194
+ {
195
+ if (collection.Count == 0)
196
+ {
197
+ continue;
198
+ }
199
+
200
+ try
201
+ {
202
+ collection.RemoveElement(element);
203
+
204
+ if (collection.Count == 0)
205
+ {
206
+ this.containersUsed--;
207
+ }
208
+
209
+ this.totalContainedItems--;
210
+ return;
211
+ }
212
+ catch (ArgumentException)
213
+ {
214
+ // Eat the exception and keep looking. We'll throw our own if we can't find its home.
215
+ }
216
+
217
+ continue;
218
+ }
219
+ }
220
+
221
+ throw new ArgumentException(String.Format(
222
+ CultureInfo.InvariantCulture,
223
+ "Element of type {0} is not valid for this collection.",
224
+ element.GetType().Name));
225
+ }
226
+
227
+ /// <summary>
228
+ /// Copies this collection to an array.
229
+ /// </summary>
230
+ /// <param name="array">Array to copy to.</param>
231
+ /// <param name="index">Offset into the array.</param>
232
+ public void CopyTo(Array array, int index)
233
+ {
234
+ int item = 0;
235
+ foreach (ISchemaElement element in this)
236
+ {
237
+ array.SetValue(element, (long)(item + index));
238
+ item++;
239
+ }
240
+ }
241
+
242
+ /// <summary>
243
+ /// Creates an enumerator for walking the elements in this collection.
244
+ /// </summary>
245
+ /// <returns>A newly created enumerator.</returns>
246
+ public IEnumerator GetEnumerator()
247
+ {
248
+ return new ElementCollectionEnumerator(this);
249
+ }
250
+
251
+ /// <summary>
252
+ /// Gets an enumerable collection of children of a given type.
253
+ /// </summary>
254
+ /// <param name="childType">Type of children to get.</param>
255
+ /// <returns>A collection of children.</returns>
256
+ /// <exception cref="ArgumentException">Thrown if the type isn't a valid child type.</exception>
257
+ public IEnumerable Filter(Type childType)
258
+ {
259
+ foreach (object container in this.items)
260
+ {
261
+ CollectionItem collectionItem = container as CollectionItem;
262
+ if (collectionItem != null)
263
+ {
264
+ if (collectionItem.ElementType.IsAssignableFrom(childType))
265
+ {
266
+ return collectionItem.Elements;
267
+ }
268
+
269
+ continue;
270
+ }
271
+
272
+ ElementCollection elementCollection = container as ElementCollection;
273
+ if (elementCollection != null)
274
+ {
275
+ IEnumerable nestedFilter = elementCollection.Filter(childType);
276
+ if (nestedFilter != null)
277
+ {
278
+ return nestedFilter;
279
+ }
280
+
281
+ continue;
282
+ }
283
+ }
284
+
285
+ throw new ArgumentException(String.Format(
286
+ CultureInfo.InvariantCulture,
287
+ "Type {0} is not valid for this collection.",
288
+ childType.Name));
289
+ }
290
+
291
+ /// <summary>
292
+ /// Adds a type to this collection.
293
+ /// </summary>
294
+ /// <param name="collectionItem">CollectionItem representing the type to add.</param>
295
+ public void AddItem(CollectionItem collectionItem)
296
+ {
297
+ this.items.Add(collectionItem);
298
+ }
299
+
300
+ /// <summary>
301
+ /// Adds a nested collection to this collection.
302
+ /// </summary>
303
+ /// <param name="collection">ElementCollection to add.</param>
304
+ public void AddCollection(ElementCollection collection)
305
+ {
306
+ this.items.Add(collection);
307
+ }
308
+
309
+ /// <summary>
310
+ /// Class used to represent a given type in the child collection of an element. Abstract,
311
+ /// has subclasses for choice and sequence (which can do cardinality checks).
312
+ /// </summary>
313
+ public abstract class CollectionItem
314
+ {
315
+ private Type elementType;
316
+ private ArrayList elements;
317
+
318
+ /// <summary>
319
+ /// Creates a new CollectionItem for the given element type.
320
+ /// </summary>
321
+ /// <param name="elementType">Type of the element for this collection item.</param>
322
+ public CollectionItem(Type elementType)
323
+ {
324
+ this.elementType = elementType;
325
+ this.elements = new ArrayList();
326
+ }
327
+
328
+ /// <summary>
329
+ /// Gets the type of this collection's items.
330
+ /// </summary>
331
+ public Type ElementType
332
+ {
333
+ get { return this.elementType; }
334
+ }
335
+
336
+ /// <summary>
337
+ /// Gets the elements of this collection.
338
+ /// </summary>
339
+ public ArrayList Elements
340
+ {
341
+ get { return this.elements; }
342
+ }
343
+
344
+ /// <summary>
345
+ /// Adds an element to this collection. Must be of an assignable type to the collection's
346
+ /// type.
347
+ /// </summary>
348
+ /// <param name="element">The element to add.</param>
349
+ /// <exception cref="ArgumentException">Thrown if the type isn't assignable to the collection's type.</exception>
350
+ public void AddElement(ISchemaElement element)
351
+ {
352
+ if (!this.elementType.IsAssignableFrom(element.GetType()))
353
+ {
354
+ throw new ArgumentException(
355
+ String.Format(
356
+ CultureInfo.InvariantCulture,
357
+ "Element must be a subclass of {0}, but was of type {1}.",
358
+ this.elementType.Name,
359
+ element.GetType().Name),
360
+ "element");
361
+ }
362
+
363
+ this.elements.Add(element);
364
+ }
365
+
366
+ /// <summary>
367
+ /// Removes an element from this collection.
368
+ /// </summary>
369
+ /// <param name="element">The element to remove.</param>
370
+ /// <exception cref="ArgumentException">Thrown if the element's type isn't assignable to the collection's type.</exception>
371
+ public void RemoveElement(ISchemaElement element)
372
+ {
373
+ if (!this.elementType.IsAssignableFrom(element.GetType()))
374
+ {
375
+ throw new ArgumentException(
376
+ String.Format(
377
+ CultureInfo.InvariantCulture,
378
+ "Element must be a subclass of {0}, but was of type {1}.",
379
+ this.elementType.Name,
380
+ element.GetType().Name),
381
+ "element");
382
+ }
383
+
384
+ this.elements.Remove(element);
385
+ }
386
+ }
387
+
388
+ /// <summary>
389
+ /// Class representing a choice item. Doesn't do cardinality checks.
390
+ /// </summary>
391
+ public class ChoiceItem : CollectionItem
392
+ {
393
+ /// <summary>
394
+ /// Creates a new choice item.
395
+ /// </summary>
396
+ /// <param name="elementType">Type of the created item.</param>
397
+ public ChoiceItem(Type elementType) : base(elementType)
398
+ {
399
+ }
400
+ }
401
+
402
+ /// <summary>
403
+ /// Class representing a sequence item. Can do cardinality checks, if required.
404
+ /// </summary>
405
+ public class SequenceItem : CollectionItem
406
+ {
407
+ private int minimum = 1;
408
+ private int maximum = 1;
409
+
410
+ /// <summary>
411
+ /// Creates a new sequence item.
412
+ /// </summary>
413
+ /// <param name="elementType">Type of the created item.</param>
414
+ public SequenceItem(Type elementType) : base(elementType)
415
+ {
416
+ }
417
+
418
+ /// <summary>
419
+ /// Creates a new sequence item with the specified minimum and maximum.
420
+ /// </summary>
421
+ /// <param name="elementType">Type of the created item.</param>
422
+ /// <param name="minimum">Minimum number of elements.</param>
423
+ /// <param name="maximum">Maximum number of elements.</param>
424
+ public SequenceItem(Type elementType, int minimum, int maximum) : base(elementType)
425
+ {
426
+ this.minimum = minimum;
427
+ this.maximum = maximum;
428
+ }
429
+ }
430
+
431
+ /// <summary>
432
+ /// Enumerator for the ElementCollection.
433
+ /// </summary>
434
+ private class ElementCollectionEnumerator : IEnumerator
435
+ {
436
+ private ElementCollection collection;
437
+ private Stack collectionStack;
438
+
439
+ /// <summary>
440
+ /// Creates a new ElementCollectionEnumerator.
441
+ /// </summary>
442
+ /// <param name="collection">The collection to create an enumerator for.</param>
443
+ public ElementCollectionEnumerator(ElementCollection collection)
444
+ {
445
+ this.collection = collection;
446
+ }
447
+
448
+ /// <summary>
449
+ /// Gets the current object from the enumerator.
450
+ /// </summary>
451
+ public object Current
452
+ {
453
+ get
454
+ {
455
+ if (this.collectionStack != null && this.collectionStack.Count > 0)
456
+ {
457
+ CollectionTuple tuple = (CollectionTuple)this.collectionStack.Peek();
458
+ object container = tuple.Collection.items[tuple.ContainerIndex];
459
+
460
+ CollectionItem collectionItem = container as CollectionItem;
461
+ if (collectionItem != null)
462
+ {
463
+ return collectionItem.Elements[tuple.ItemIndex];
464
+ }
465
+
466
+ throw new InvalidOperationException(String.Format(
467
+ CultureInfo.InvariantCulture,
468
+ "Element of type {0} found in enumerator. Must be ChoiceItem or SequenceItem.",
469
+ container.GetType().Name));
470
+ }
471
+
472
+ return null;
473
+ }
474
+ }
475
+
476
+ /// <summary>
477
+ /// Resets the enumerator to the beginning.
478
+ /// </summary>
479
+ public void Reset()
480
+ {
481
+ if (this.collectionStack != null)
482
+ {
483
+ this.collectionStack.Clear();
484
+ this.collectionStack = null;
485
+ }
486
+ }
487
+
488
+ /// <summary>
489
+ /// Moves the enumerator to the next item.
490
+ /// </summary>
491
+ /// <returns>True if there is a next item, false otherwise.</returns>
492
+ public bool MoveNext()
493
+ {
494
+ if (this.collectionStack == null)
495
+ {
496
+ if (this.collection.Count == 0)
497
+ {
498
+ return false;
499
+ }
500
+
501
+ this.collectionStack = new Stack();
502
+ this.collectionStack.Push(new CollectionTuple(this.collection));
503
+ }
504
+
505
+ CollectionTuple tuple = (CollectionTuple)this.collectionStack.Peek();
506
+
507
+ if (this.FindNext(tuple))
508
+ {
509
+ return true;
510
+ }
511
+
512
+ this.collectionStack.Pop();
513
+ if (this.collectionStack.Count == 0)
514
+ {
515
+ return false;
516
+ }
517
+
518
+ return this.MoveNext();
519
+ }
520
+
521
+ /// <summary>
522
+ /// Pushes a collection onto the stack.
523
+ /// </summary>
524
+ /// <param name="collection">The collection to push.</param>
525
+ private void PushCollection(ElementCollection collection)
526
+ {
527
+ if (collection.Count <= 0)
528
+ {
529
+ throw new ArgumentException(String.Format(
530
+ CultureInfo.InvariantCulture,
531
+ "Collection has {0} elements. Must have at least one.",
532
+ collection.Count));
533
+ }
534
+
535
+ CollectionTuple tuple = new CollectionTuple(collection);
536
+ this.collectionStack.Push(tuple);
537
+ this.FindNext(tuple);
538
+ }
539
+
540
+ /// <summary>
541
+ /// Finds the next item from a given tuple.
542
+ /// </summary>
543
+ /// <param name="tuple">The tuple to start looking from.</param>
544
+ /// <returns>True if a next element is found, false otherwise.</returns>
545
+ private bool FindNext(CollectionTuple tuple)
546
+ {
547
+ object container = tuple.Collection.items[tuple.ContainerIndex];
548
+
549
+ CollectionItem collectionItem = container as CollectionItem;
550
+ if (collectionItem != null)
551
+ {
552
+ if (tuple.ItemIndex + 1 < collectionItem.Elements.Count)
553
+ {
554
+ tuple.ItemIndex++;
555
+ return true;
556
+ }
557
+ }
558
+
559
+ ElementCollection elementCollection = container as ElementCollection;
560
+ if (elementCollection != null && elementCollection.Count > 0 && tuple.ItemIndex == -1)
561
+ {
562
+ tuple.ItemIndex++;
563
+ this.PushCollection(elementCollection);
564
+ return true;
565
+ }
566
+
567
+ tuple.ItemIndex = 0;
568
+
569
+ for (int i = tuple.ContainerIndex + 1; i < tuple.Collection.items.Count; ++i)
570
+ {
571
+ object nestedContainer = tuple.Collection.items[i];
572
+
573
+ CollectionItem nestedCollectionItem = nestedContainer as CollectionItem;
574
+ if (nestedCollectionItem != null)
575
+ {
576
+ if (nestedCollectionItem.Elements.Count > 0)
577
+ {
578
+ tuple.ContainerIndex = i;
579
+ return true;
580
+ }
581
+ }
582
+
583
+ ElementCollection nestedElementCollection = nestedContainer as ElementCollection;
584
+ if (nestedElementCollection != null && nestedElementCollection.Count > 0)
585
+ {
586
+ tuple.ContainerIndex = i;
587
+ this.PushCollection(nestedElementCollection);
588
+ return true;
589
+ }
590
+ }
591
+
592
+ return false;
593
+ }
594
+
595
+ /// <summary>
596
+ /// Class representing a single point in the collection. Consists of an ElementCollection,
597
+ /// a container index, and an index into the container.
598
+ /// </summary>
599
+ private class CollectionTuple
600
+ {
601
+ private ElementCollection collection;
602
+ private int containerIndex;
603
+ private int itemIndex = -1;
604
+
605
+ /// <summary>
606
+ /// Creates a new CollectionTuple.
607
+ /// </summary>
608
+ /// <param name="collection">The collection for the tuple.</param>
609
+ public CollectionTuple(ElementCollection collection)
610
+ {
611
+ this.collection = collection;
612
+ }
613
+
614
+ /// <summary>
615
+ /// Gets the collection for the tuple.
616
+ /// </summary>
617
+ public ElementCollection Collection
618
+ {
619
+ get { return this.collection; }
620
+ }
621
+
622
+ /// <summary>
623
+ /// Gets and sets the index of the container in the collection.
624
+ /// </summary>
625
+ public int ContainerIndex
626
+ {
627
+ get { return this.containerIndex; }
628
+ set { this.containerIndex = value; }
629
+ }
630
+
631
+ /// <summary>
632
+ /// Gets and sets the index of the item in the container.
633
+ /// </summary>
634
+ public int ItemIndex
635
+ {
636
+ get { return this.itemIndex; }
637
+ set { this.itemIndex = value; }
638
+ }
639
+ }
640
+ }
641
+ }
642
+}
src/WixBuildTools.XsdGen/StronglyTypedClasses.cs
new
+1498
@@ -0,0 +1,1498 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Tools
4
+{
5
+ using System;
6
+ using System.CodeDom;
7
+ using System.Collections;
8
+ using System.Collections.Generic;
9
+ using System.Collections.Specialized;
10
+ using System.IO;
11
+ using System.Reflection;
12
+ using System.Text;
13
+ using System.Text.RegularExpressions;
14
+ using System.Xml;
15
+ using System.Xml.Schema;
16
+
17
+ /// <summary>
18
+ /// Type containing static Generate method, which fills in a compile unit from a
19
+ /// given schema.
20
+ /// </summary>
21
+ internal class StronglyTypedClasses
22
+ {
23
+ private static string outputXmlComment = "Processes this element and all child elements into an XmlWriter.";
24
+ private static Hashtable simpleTypeNamesToClrTypeNames;
25
+ private static Dictionary<string, EnumDeclaration> typeNamesToEnumDeclarations;
26
+ private static Dictionary<EnumDeclaration, CodeTypeDeclaration> enumsToParseMethodClasses;
27
+ private static Regex multiUppercaseNameRegex = new Regex("[A-Z][A-Z][A-Z]", RegexOptions.Compiled);
28
+ private static Dictionary<string, XmlSchemaAttributeGroup> refToAttributeGroups;
29
+ private static CodeTypeDeclaration enumHelperClass;
30
+
31
+ /// <summary>
32
+ /// Private constructor for static class.
33
+ /// </summary>
34
+ private StronglyTypedClasses()
35
+ {
36
+ }
37
+
38
+ /// <summary>
39
+ /// Generates strongly typed serialization classes for the given schema document
40
+ /// under the given namespace and generates a code compile unit.
41
+ /// </summary>
42
+ /// <param name="xmlSchema">Schema document to generate classes for.</param>
43
+ /// <param name="generateNamespace">Namespace to be used for the generated code.</param>
44
+ /// <param name="commonNamespace">Namespace in which to find common classes and interfaces,
45
+ /// like ISchemaElement.</param>
46
+ /// <returns>A fully populated CodeCompileUnit, which can be serialized in the language of choice.</returns>
47
+ public static CodeCompileUnit Generate(XmlSchema xmlSchema, string generateNamespace, string commonNamespace)
48
+ {
49
+ if (xmlSchema == null)
50
+ {
51
+ throw new ArgumentNullException("xmlSchema");
52
+ }
53
+ if (generateNamespace == null)
54
+ {
55
+ throw new ArgumentNullException("generateNamespace");
56
+ }
57
+
58
+ simpleTypeNamesToClrTypeNames = new Hashtable();
59
+ typeNamesToEnumDeclarations = new Dictionary<string, EnumDeclaration>();
60
+ refToAttributeGroups = new Dictionary<string, XmlSchemaAttributeGroup>();
61
+ enumsToParseMethodClasses = new Dictionary<EnumDeclaration, CodeTypeDeclaration>();
62
+
63
+ CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
64
+ CodeNamespace codeNamespace = new CodeNamespace(generateNamespace);
65
+ codeCompileUnit.Namespaces.Add(codeNamespace);
66
+ codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
67
+ codeNamespace.Imports.Add(new CodeNamespaceImport("System.CodeDom.Compiler")); // for GeneratedCodeAttribute
68
+ codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections"));
69
+ codeNamespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics.CodeAnalysis"));
70
+ codeNamespace.Imports.Add(new CodeNamespaceImport("System.Globalization"));
71
+ codeNamespace.Imports.Add(new CodeNamespaceImport("System.Xml"));
72
+ if (commonNamespace != null)
73
+ {
74
+ codeNamespace.Imports.Add(new CodeNamespaceImport(commonNamespace));
75
+ }
76
+
77
+ // NOTE: This hash table serves double duty so be sure to have the XSD
78
+ // type name mapped to the CLR type name *and* the CLR type name
79
+ // mapped to the same CLR type name. Look at long and bool for
80
+ // examples below (and before you ask, no I don't know why DateTime
81
+ // just works).
82
+ simpleTypeNamesToClrTypeNames.Add("dateTime", "DateTime");
83
+ simpleTypeNamesToClrTypeNames.Add("integer", "int");
84
+ simpleTypeNamesToClrTypeNames.Add("int", "int");
85
+ simpleTypeNamesToClrTypeNames.Add("NMTOKEN", "string");
86
+ simpleTypeNamesToClrTypeNames.Add("string", "string");
87
+ simpleTypeNamesToClrTypeNames.Add("nonNegativeInteger", "long");
88
+ simpleTypeNamesToClrTypeNames.Add("long", "long");
89
+ simpleTypeNamesToClrTypeNames.Add("boolean", "bool");
90
+ simpleTypeNamesToClrTypeNames.Add("bool", "bool");
91
+
92
+ xmlSchema.Compile(null);
93
+
94
+ foreach (XmlSchemaAttributeGroup schemaAttributeGroup in xmlSchema.AttributeGroups.Values)
95
+ {
96
+ refToAttributeGroups.Add(schemaAttributeGroup.Name, schemaAttributeGroup);
97
+ }
98
+
99
+ foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values)
100
+ {
101
+ XmlSchemaSimpleType schemaSimpleType = schemaObject as XmlSchemaSimpleType;
102
+ if (schemaSimpleType != null)
103
+ {
104
+ ProcessSimpleType(schemaSimpleType, codeNamespace);
105
+ }
106
+ }
107
+
108
+ foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values)
109
+ {
110
+ XmlSchemaComplexType schemaComplexType = schemaObject as XmlSchemaComplexType;
111
+ if (schemaComplexType != null)
112
+ {
113
+ ProcessComplexType(schemaComplexType, codeNamespace);
114
+ }
115
+ }
116
+
117
+ foreach (XmlSchemaObject schemaObject in xmlSchema.Elements.Values)
118
+ {
119
+ XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement;
120
+ if (schemaElement != null)
121
+ {
122
+ ProcessElement(schemaElement, codeNamespace);
123
+ }
124
+ }
125
+
126
+ return codeCompileUnit;
127
+ }
128
+
129
+ /// <summary>
130
+ /// Processes an XmlSchemaElement into corresponding types.
131
+ /// </summary>
132
+ /// <param name="schemaElement">XmlSchemaElement to be processed.</param>
133
+ /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
134
+ private static void ProcessElement(XmlSchemaElement schemaElement, CodeNamespace codeNamespace)
135
+ {
136
+ string elementType = schemaElement.SchemaTypeName.Name;
137
+ string elementNamespace = schemaElement.QualifiedName.Namespace;
138
+ string elementDocumentation = GetDocumentation(schemaElement.Annotation);
139
+
140
+ if ((elementType == null || elementType.Length == 0) && schemaElement.SchemaType != null)
141
+ {
142
+ ProcessComplexType(schemaElement.Name, elementNamespace, (XmlSchemaComplexType)schemaElement.SchemaType, elementDocumentation, codeNamespace);
143
+ }
144
+ else
145
+ {
146
+ if (elementType == null || elementType.Length == 0)
147
+ {
148
+ elementType = "string";
149
+ }
150
+
151
+ CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(schemaElement.Name);
152
+ typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
153
+ typeDeclaration.Attributes = MemberAttributes.Public;
154
+ typeDeclaration.IsClass = true;
155
+
156
+ if (elementDocumentation != null)
157
+ {
158
+ GenerateSummaryComment(typeDeclaration.Comments, elementDocumentation);
159
+ }
160
+
161
+ CodeMemberMethod outputXmlMethod = new CodeMemberMethod();
162
+ outputXmlMethod.Attributes = MemberAttributes.Public;
163
+ outputXmlMethod.ImplementationTypes.Add("ISchemaElement");
164
+ outputXmlMethod.Name = "OutputXml";
165
+ outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer"));
166
+ outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false));
167
+ outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", schemaElement.Name, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\""))));
168
+ GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment);
169
+
170
+ if (simpleTypeNamesToClrTypeNames.ContainsKey(elementType))
171
+ {
172
+ CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement");
173
+ typeDeclaration.Members.Add(parentField);
174
+
175
+ CodeMemberProperty parentProperty = new CodeMemberProperty();
176
+ parentProperty.Attributes = MemberAttributes.Public;
177
+ parentProperty.ImplementationTypes.Add("ISchemaElement");
178
+ parentProperty.Name = "ParentElement";
179
+ parentProperty.Type = new CodeTypeReference("ISchemaElement");
180
+ parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement")));
181
+ parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value")));
182
+ typeDeclaration.Members.Add(parentProperty);
183
+
184
+ CodeMemberMethod setAttributeMethod = new CodeMemberMethod();
185
+ setAttributeMethod.Attributes = MemberAttributes.Public;
186
+ setAttributeMethod.ImplementationTypes.Add("ISetAttributes");
187
+ setAttributeMethod.Name = "SetAttribute";
188
+ setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
189
+ setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value"));
190
+ setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes");
191
+ setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
192
+ setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true));
193
+
194
+ GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[elementType], typeDeclaration, outputXmlMethod, setAttributeMethod, null, elementDocumentation, true, false);
195
+
196
+ typeDeclaration.Members.Add(setAttributeMethod);
197
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes"));
198
+ }
199
+ else
200
+ {
201
+ typeDeclaration.BaseTypes.Add(elementType);
202
+ outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "OutputXml", new CodeVariableReferenceExpression("writer")));
203
+ outputXmlMethod.Attributes |= MemberAttributes.Override;
204
+ }
205
+
206
+ outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement"));
207
+
208
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement"));
209
+ typeDeclaration.Members.Add(outputXmlMethod);
210
+ codeNamespace.Types.Add(typeDeclaration);
211
+ }
212
+ }
213
+
214
+ /// <summary>
215
+ /// Processes an XmlSchemaComplexType into corresponding types.
216
+ /// </summary>
217
+ /// <param name="complexType">XmlSchemaComplexType to be processed.</param>
218
+ /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
219
+ private static void ProcessComplexType(XmlSchemaComplexType complexType, CodeNamespace codeNamespace)
220
+ {
221
+ CodeMemberMethod outputXmlMethod = new CodeMemberMethod();
222
+ outputXmlMethod.Attributes = MemberAttributes.Public;
223
+ outputXmlMethod.ImplementationTypes.Add("ISchemaElement");
224
+ outputXmlMethod.Name = "OutputXml";
225
+ outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer"));
226
+ outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false));
227
+ GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment);
228
+
229
+ CodeMemberMethod setAttributeMethod = new CodeMemberMethod();
230
+ setAttributeMethod.Attributes = MemberAttributes.Public;
231
+ setAttributeMethod.ImplementationTypes.Add("ISetAttributes");
232
+ setAttributeMethod.Name = "SetAttribute";
233
+ setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
234
+ setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value"));
235
+ setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes");
236
+ setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
237
+ setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true));
238
+
239
+ string documentation = GetDocumentation(complexType.Annotation);
240
+
241
+ ProcessSimpleContent(complexType.Name, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, true);
242
+ }
243
+
244
+ /// <summary>
245
+ /// Processes an XmlSchemaComplexType into corresponding types.
246
+ /// </summary>
247
+ /// <param name="typeName">Name to use for the type being output.</param>
248
+ /// <param name="elementNamespace">Namespace of the xml element.</param>
249
+ /// <param name="complexType">XmlSchemaComplexType to be processed.</param>
250
+ /// <param name="documentation">Documentation for the element.</param>
251
+ /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
252
+ private static void ProcessComplexType(string typeName, string elementNamespace, XmlSchemaComplexType complexType, string documentation, CodeNamespace codeNamespace)
253
+ {
254
+ CodeMemberMethod outputXmlMethod = new CodeMemberMethod();
255
+ outputXmlMethod.Attributes = MemberAttributes.Public;
256
+ outputXmlMethod.ImplementationTypes.Add("ISchemaElement");
257
+ outputXmlMethod.Name = "OutputXml";
258
+ outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer"));
259
+ outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false));
260
+ outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", typeName, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\""))));
261
+ GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment);
262
+
263
+ CodeMemberMethod setAttributeMethod = new CodeMemberMethod();
264
+ setAttributeMethod.Attributes = MemberAttributes.Public;
265
+ setAttributeMethod.ImplementationTypes.Add("ISetAttributes");
266
+ setAttributeMethod.Name = "SetAttribute";
267
+ setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
268
+ setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value"));
269
+ setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes");
270
+ setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
271
+ setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true));
272
+
273
+ if (complexType.ContentModel == null)
274
+ {
275
+ CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName);
276
+ typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
277
+ typeDeclaration.Attributes = MemberAttributes.Public;
278
+ typeDeclaration.IsClass = true;
279
+ CodeIterationStatement childEnumStatement = null;
280
+
281
+ if (documentation != null)
282
+ {
283
+ GenerateSummaryComment(typeDeclaration.Comments, documentation);
284
+ }
285
+
286
+ if (complexType.Particle != null)
287
+ {
288
+ CodeMemberField childrenField = new CodeMemberField("ElementCollection", "children");
289
+ typeDeclaration.Members.Add(childrenField);
290
+
291
+ CodeMemberProperty childrenProperty = new CodeMemberProperty();
292
+ childrenProperty.Attributes = MemberAttributes.Public;
293
+ childrenProperty.ImplementationTypes.Add("IParentElement");
294
+ childrenProperty.Name = "Children";
295
+ childrenProperty.Type = new CodeTypeReference("IEnumerable");
296
+ childrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children")));
297
+ typeDeclaration.Members.Add(childrenProperty);
298
+
299
+ CodeMemberProperty filterChildrenProperty = new CodeMemberProperty();
300
+ filterChildrenProperty.Attributes = MemberAttributes.Public;
301
+ filterChildrenProperty.ImplementationTypes.Add("IParentElement");
302
+ filterChildrenProperty.Name = "Item";
303
+ filterChildrenProperty.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Type), "childType"));
304
+ filterChildrenProperty.Type = new CodeTypeReference("IEnumerable");
305
+ filterChildrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "Filter", new CodeVariableReferenceExpression("childType"))));
306
+ filterChildrenProperty.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers"));
307
+ typeDeclaration.Members.Add(filterChildrenProperty);
308
+
309
+ CodeMemberMethod addChildMethod = new CodeMemberMethod();
310
+ addChildMethod.Attributes = MemberAttributes.Public;
311
+ addChildMethod.ImplementationTypes.Add("IParentElement");
312
+ addChildMethod.Name = "AddChild";
313
+ addChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child"));
314
+ addChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false));
315
+ CodeExpressionStatement addChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "AddElement", new CodeVariableReferenceExpression("child")));
316
+ addChildMethod.Statements.Add(addChildStatement);
317
+ CodeAssignStatement setParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodeThisReferenceExpression());
318
+ addChildMethod.Statements.Add(setParentStatement);
319
+ typeDeclaration.Members.Add(addChildMethod);
320
+
321
+ CodeMemberMethod removeChildMethod = new CodeMemberMethod();
322
+ removeChildMethod.Attributes = MemberAttributes.Public;
323
+ removeChildMethod.ImplementationTypes.Add("IParentElement");
324
+ removeChildMethod.Name = "RemoveChild";
325
+ removeChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child"));
326
+ removeChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false));
327
+ CodeExpressionStatement removeChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "RemoveElement", new CodeVariableReferenceExpression("child")));
328
+ removeChildMethod.Statements.Add(removeChildStatement);
329
+ CodeAssignStatement nullParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodePrimitiveExpression(null));
330
+ removeChildMethod.Statements.Add(nullParentStatement);
331
+ typeDeclaration.Members.Add(removeChildMethod);
332
+
333
+ CodeMemberMethod createChildMethod = new CodeMemberMethod();
334
+ createChildMethod.Attributes = MemberAttributes.Public;
335
+ createChildMethod.ImplementationTypes.Add("ICreateChildren");
336
+ createChildMethod.Name = "CreateChild";
337
+ createChildMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "childName"));
338
+ createChildMethod.PrivateImplementationType = new CodeTypeReference("ICreateChildren");
339
+ createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
340
+ createChildMethod.ReturnType = new CodeTypeReference("ISchemaElement");
341
+ createChildMethod.Statements.Add(GetArgumentNullCheckStatement("childName", true));
342
+ createChildMethod.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childValue", new CodePrimitiveExpression(null)));
343
+
344
+ CodeConstructor typeConstructor = new CodeConstructor();
345
+ typeConstructor.Attributes = MemberAttributes.Public;
346
+
347
+ CodeVariableReferenceExpression collectionVariable = null;
348
+
349
+ XmlSchemaChoice schemaChoice = complexType.Particle as XmlSchemaChoice;
350
+ if (schemaChoice != null)
351
+ {
352
+ collectionVariable = ProcessSchemaGroup(schemaChoice, typeConstructor, createChildMethod);
353
+ }
354
+ else
355
+ {
356
+ XmlSchemaSequence schemaSequence = complexType.Particle as XmlSchemaSequence;
357
+ if (schemaSequence != null)
358
+ {
359
+ collectionVariable = ProcessSchemaGroup(schemaSequence, typeConstructor, createChildMethod);
360
+ }
361
+ }
362
+
363
+ typeConstructor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), collectionVariable));
364
+ typeDeclaration.Members.Add(typeConstructor);
365
+
366
+ CodeConditionStatement childNameNotFound = new CodeConditionStatement();
367
+ childNameNotFound.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childValue"));
368
+ childNameNotFound.TrueStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression("InvalidOperationException", new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("String"), "Concat", new CodeVariableReferenceExpression("childName"), new CodeSnippetExpression("\" is not a valid child name.\"")))));
369
+ createChildMethod.Statements.Add(childNameNotFound);
370
+
371
+ if (createChildMethod.Statements.Count > 8)
372
+ {
373
+ createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
374
+ }
375
+
376
+ createChildMethod.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("childValue")));
377
+ typeDeclaration.Members.Add(createChildMethod);
378
+
379
+ childEnumStatement = new CodeIterationStatement();
380
+ childEnumStatement.InitStatement = new CodeVariableDeclarationStatement("IEnumerator", "enumerator", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "GetEnumerator"));
381
+ childEnumStatement.TestExpression = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext");
382
+ childEnumStatement.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childElement", new CodeCastExpression("ISchemaElement", new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current"))));
383
+ childEnumStatement.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("childElement"), "OutputXml", new CodeVariableReferenceExpression("writer")));
384
+ childEnumStatement.IncrementStatement = new CodeExpressionStatement(new CodeSnippetExpression(""));
385
+
386
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("IParentElement"));
387
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ICreateChildren"));
388
+ }
389
+
390
+ // TODO: Handle xs:anyAttribute.
391
+ ProcessAttributes(complexType.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod);
392
+
393
+ if (childEnumStatement != null)
394
+ {
395
+ outputXmlMethod.Statements.Add(childEnumStatement);
396
+ }
397
+
398
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement"));
399
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes"));
400
+
401
+ CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement");
402
+ typeDeclaration.Members.Add(parentField);
403
+
404
+ CodeMemberProperty parentProperty = new CodeMemberProperty();
405
+ parentProperty.Attributes = MemberAttributes.Public;
406
+ parentProperty.ImplementationTypes.Add("ISchemaElement");
407
+ parentProperty.Name = "ParentElement";
408
+ parentProperty.Type = new CodeTypeReference("ISchemaElement");
409
+ parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement")));
410
+ parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value")));
411
+ typeDeclaration.Members.Add(parentProperty);
412
+
413
+ if (outputXmlMethod.Statements.Count > 8)
414
+ {
415
+ outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
416
+ }
417
+
418
+ if (setAttributeMethod.Statements.Count > 8)
419
+ {
420
+ setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
421
+ }
422
+
423
+ typeDeclaration.Members.Add(outputXmlMethod);
424
+ typeDeclaration.Members.Add(setAttributeMethod);
425
+ codeNamespace.Types.Add(typeDeclaration);
426
+ }
427
+ else
428
+ {
429
+ ProcessSimpleContent(typeName, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, false);
430
+ }
431
+
432
+ outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement"));
433
+ }
434
+
435
+ /// <summary>
436
+ /// Processes a collection of attributes, generating the required fields and properties.
437
+ /// </summary>
438
+ /// <param name="attributes">List of attribute or attributeGroupRef elements being processed.</param>
439
+ /// <param name="typeDeclaration">CodeTypeDeclaration to be used when outputting code.</param>
440
+ /// <param name="outputXmlMethod">Member method for the OutputXml method.</param>
441
+ /// <param name="setAttributeMethod">Member method for the SetAttribute method.</param>
442
+ private static void ProcessAttributes(XmlSchemaObjectCollection attributes, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod)
443
+ {
444
+ foreach (XmlSchemaObject schemaObject in attributes)
445
+ {
446
+ XmlSchemaAttribute schemaAttribute = schemaObject as XmlSchemaAttribute;
447
+ if (schemaAttribute != null)
448
+ {
449
+ ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod);
450
+ }
451
+ else
452
+ {
453
+ XmlSchemaAttributeGroupRef schemaAttributeGroupRef = schemaObject as XmlSchemaAttributeGroupRef;
454
+ if (schemaAttributeGroupRef != null)
455
+ {
456
+ XmlSchemaAttributeGroup schemaAttributeGroup = refToAttributeGroups[schemaAttributeGroupRef.RefName.Name];
457
+ // recurse!
458
+ ProcessAttributes(schemaAttributeGroup.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod);
459
+ }
460
+ }
461
+ }
462
+ }
463
+
464
+ /// <summary>
465
+ /// Processes an XmlSchemaGroupBase element.
466
+ /// </summary>
467
+ /// <param name="schemaGroup">Element group to process.</param>
468
+ /// <param name="constructor">Constructor to which statements should be added.</param>
469
+ /// <param name="createChildMethod">Method used for creating children on read-in.</param>
470
+ /// <returns>A reference to the local variable containing the collection.</returns>
471
+ private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod)
472
+ {
473
+ return ProcessSchemaGroup(schemaGroup, constructor, createChildMethod, 0);
474
+ }
475
+
476
+ /// <summary>
477
+ /// Processes an XmlSchemaGroupBase element.
478
+ /// </summary>
479
+ /// <param name="schemaGroup">Element group to process.</param>
480
+ /// <param name="constructor">Constructor to which statements should be added.</param>
481
+ /// <param name="createChildMethod">Method used for creating children on read-in.</param>
482
+ /// <param name="depth">Depth to which this collection is nested.</param>
483
+ /// <returns>A reference to the local variable containing the collection.</returns>
484
+ private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod, int depth)
485
+ {
486
+ string collectionName = String.Format("childCollection{0}", depth);
487
+ CodeVariableReferenceExpression collectionVariableReference = new CodeVariableReferenceExpression(collectionName);
488
+ CodeVariableDeclarationStatement collectionStatement = new CodeVariableDeclarationStatement("ElementCollection", collectionName);
489
+ if (schemaGroup is XmlSchemaChoice)
490
+ {
491
+ collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Choice"));
492
+ }
493
+ else
494
+ {
495
+ collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Sequence"));
496
+ }
497
+ constructor.Statements.Add(collectionStatement);
498
+
499
+ foreach (XmlSchemaObject obj in schemaGroup.Items)
500
+ {
501
+ XmlSchemaElement schemaElement = obj as XmlSchemaElement;
502
+ if (schemaElement != null)
503
+ {
504
+ if (schemaGroup is XmlSchemaChoice)
505
+ {
506
+ CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression(schemaElement.RefName.Name)));
507
+ constructor.Statements.Add(addItemInvoke);
508
+ }
509
+ else
510
+ {
511
+ CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression(schemaElement.RefName.Name)));
512
+ constructor.Statements.Add(addItemInvoke);
513
+ }
514
+
515
+ CodeConditionStatement createChildIf = new CodeConditionStatement();
516
+ createChildIf.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", schemaElement.RefName.Name, "\"")), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childName"));
517
+ createChildIf.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("childValue"), new CodeObjectCreateExpression(schemaElement.RefName.Name)));
518
+ createChildMethod.Statements.Add(createChildIf);
519
+
520
+ continue;
521
+ }
522
+
523
+ XmlSchemaAny schemaAny = obj as XmlSchemaAny;
524
+ if (schemaAny != null)
525
+ {
526
+ if (schemaGroup is XmlSchemaChoice)
527
+ {
528
+ CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression("ISchemaElement")));
529
+ constructor.Statements.Add(addItemInvoke);
530
+ }
531
+ else
532
+ {
533
+ CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression("ISchemaElement"), new CodeSnippetExpression("0"), new CodeSnippetExpression("-1")));
534
+ constructor.Statements.Add(addItemInvoke);
535
+ }
536
+
537
+ continue;
538
+ }
539
+
540
+ XmlSchemaGroupBase schemaGroupBase = obj as XmlSchemaGroupBase;
541
+ if (schemaGroupBase != null)
542
+ {
543
+ CodeVariableReferenceExpression nestedCollectionReference = ProcessSchemaGroup(schemaGroupBase, constructor, createChildMethod, depth + 1);
544
+ CodeMethodInvokeExpression addCollectionInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddCollection", nestedCollectionReference);
545
+ constructor.Statements.Add(addCollectionInvoke);
546
+
547
+ continue;
548
+ }
549
+ }
550
+
551
+ return collectionVariableReference;
552
+ }
553
+
554
+ /// <summary>
555
+ /// Processes an XmlSchemaSimpleContentExtension into corresponding types.
556
+ /// </summary>
557
+ /// <param name="typeName">Name of the type being generated.</param>
558
+ /// <param name="simpleContent">XmlSchemaSimpleContentExtension being processed.</param>
559
+ /// <param name="documentation">Documentation for the simple content.</param>
560
+ /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
561
+ /// <param name="outputXmlMethod">Method to use when outputting Xml.</param>
562
+ /// <param name="setAttributeMethod">Method to use when setting an attribute.</param>
563
+ /// <param name="abstractClass">If true, generate an abstract class.</param>
564
+ private static void ProcessSimpleContent(string typeName, XmlSchemaSimpleContentExtension simpleContent, string documentation, CodeNamespace codeNamespace, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, bool abstractClass)
565
+ {
566
+ CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName);
567
+ typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
568
+ typeDeclaration.Attributes = MemberAttributes.Public;
569
+ typeDeclaration.IsClass = true;
570
+
571
+ if (documentation != null)
572
+ {
573
+ GenerateSummaryComment(typeDeclaration.Comments, documentation);
574
+ }
575
+
576
+ if (abstractClass)
577
+ {
578
+ typeDeclaration.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public;
579
+ }
580
+
581
+ // TODO: Handle xs:anyAttribute here.
582
+ foreach (XmlSchemaAttribute schemaAttribute in simpleContent.Attributes)
583
+ {
584
+ ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod);
585
+ }
586
+
587
+ // This needs to come last, so that the generation code generates the inner content after the attributes.
588
+ string contentDocumentation = GetDocumentation(simpleContent.Annotation);
589
+ GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[simpleContent.BaseTypeName.Name], typeDeclaration, outputXmlMethod, setAttributeMethod, null, contentDocumentation, true, false);
590
+
591
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement"));
592
+ typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes"));
593
+
594
+ CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement");
595
+ typeDeclaration.Members.Add(parentField);
596
+
597
+ CodeMemberProperty parentProperty = new CodeMemberProperty();
598
+ parentProperty.Attributes = MemberAttributes.Public;
599
+ parentProperty.ImplementationTypes.Add("ISchemaElement");
600
+ parentProperty.Name = "ParentElement";
601
+ parentProperty.Type = new CodeTypeReference("ISchemaElement");
602
+ parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement")));
603
+ parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value")));
604
+ typeDeclaration.Members.Add(parentProperty);
605
+
606
+ if (outputXmlMethod.Statements.Count > 8)
607
+ {
608
+ outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
609
+ }
610
+
611
+ if (setAttributeMethod.Statements.Count > 8)
612
+ {
613
+ setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
614
+ }
615
+
616
+ typeDeclaration.Members.Add(outputXmlMethod);
617
+ typeDeclaration.Members.Add(setAttributeMethod);
618
+ codeNamespace.Types.Add(typeDeclaration);
619
+ }
620
+
621
+ /// <summary>
622
+ /// Processes an attribute, generating the required field and property. Potentially generates
623
+ /// an enum for an attribute restriction.
624
+ /// </summary>
625
+ /// <param name="attribute">Attribute element being processed.</param>
626
+ /// <param name="typeDeclaration">CodeTypeDeclaration to be used when outputting code.</param>
627
+ /// <param name="outputXmlMethod">Member method for the OutputXml method.</param>
628
+ /// <param name="setAttributeMethod">Member method for the SetAttribute method.</param>
629
+ private static void ProcessAttribute(XmlSchemaAttribute attribute, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod)
630
+ {
631
+ string attributeName = attribute.QualifiedName.Name;
632
+ string rawAttributeType = attribute.AttributeSchemaType.QualifiedName.Name;
633
+ string attributeType = null;
634
+ EnumDeclaration enumDeclaration = null;
635
+ if (rawAttributeType == null || rawAttributeType.Length == 0)
636
+ {
637
+ ProcessSimpleType(attributeName, attribute.AttributeSchemaType, true, out enumDeclaration, out attributeType);
638
+
639
+ if (enumDeclaration != null)
640
+ {
641
+ typeDeclaration.Members.Add(enumDeclaration.TypeDeclaration);
642
+ AddEnumHelperMethods(enumDeclaration, typeDeclaration);
643
+ }
644
+ }
645
+ else
646
+ {
647
+ attributeType = (string)simpleTypeNamesToClrTypeNames[rawAttributeType];
648
+ }
649
+
650
+ string documentation = GetDocumentation(attribute.Annotation);
651
+
652
+ // TODO: Handle required fields.
653
+ GenerateFieldAndProperty(attributeName, attributeType, typeDeclaration, outputXmlMethod, setAttributeMethod, enumDeclaration, documentation, false, false);
654
+ }
655
+
656
+ /// <summary>
657
+ /// Gets the first sentence of a documentation element and returns it as a string.
658
+ /// </summary>
659
+ /// <param name="annotation">The annotation in which to look for a documentation element.</param>
660
+ /// <returns>The string representing the first sentence, or null if none found.</returns>
661
+ private static string GetDocumentation(XmlSchemaAnnotation annotation)
662
+ {
663
+ string documentation = null;
664
+
665
+ if (annotation != null && annotation.Items != null)
666
+ {
667
+ foreach (XmlSchemaObject obj in annotation.Items)
668
+ {
669
+ XmlSchemaDocumentation schemaDocumentation = obj as XmlSchemaDocumentation;
670
+ if (schemaDocumentation != null)
671
+ {
672
+ if (schemaDocumentation.Markup.Length > 0)
673
+ {
674
+ XmlText text = schemaDocumentation.Markup[0] as XmlText;
675
+ if (text != null)
676
+ {
677
+ documentation = text.Value;
678
+ }
679
+ }
680
+ break;
681
+ }
682
+ }
683
+ }
684
+
685
+ if (documentation != null)
686
+ {
687
+ documentation = documentation.Trim();
688
+ }
689
+ return documentation;
690
+ }
691
+
692
+ /// <summary>
693
+ /// Makes a valid enum value out of the passed in value. May remove spaces, add 'Item' to the
694
+ /// start if it begins with an integer, or strip out punctuation.
695
+ /// </summary>
696
+ /// <param name="enumValue">Enum value to be processed.</param>
697
+ /// <returns>Enum value with invalid characters removed.</returns>
698
+ private static string MakeEnumValue(string enumValue)
699
+ {
700
+ if (Char.IsDigit(enumValue[0]))
701
+ {
702
+ enumValue = String.Concat("Item", enumValue);
703
+ }
704
+
705
+ StringBuilder newValue = new StringBuilder();
706
+ for (int i = 0; i < enumValue.Length; ++i)
707
+ {
708
+ if (!Char.IsPunctuation(enumValue[i]) && !Char.IsSymbol(enumValue[i]) && !Char.IsWhiteSpace(enumValue[i]))
709
+ {
710
+ newValue.Append(enumValue[i]);
711
+ }
712
+ }
713
+
714
+ return newValue.ToString();
715
+ }
716
+
717
+ /// <summary>
718
+ /// Generates the private field and public property for a piece of data.
719
+ /// </summary>
720
+ /// <param name="propertyName">Name of the property being generated.</param>
721
+ /// <param name="typeName">Name of the type for the property.</param>
722
+ /// <param name="typeDeclaration">Type declaration into which the field and property should be placed.</param>
723
+ /// <param name="outputXmlMethod">Member method for the OutputXml method.</param>
724
+ /// <param name="setAttributeMethod">Member method for the SetAttribute method.</param>
725
+ /// <param name="enumDeclaration">EnumDeclaration, which is null unless called from a locally defined enum attribute.</param>
726
+ /// <param name="documentation">Comment string to be placed on the property.</param>
727
+ /// <param name="nestedContent">If true, the field will be placed in nested content when outputting to XML.</param>
728
+ /// <param name="requiredField">If true, the generated serialization code will throw if the field is not set.</param>
729
+ private static void GenerateFieldAndProperty(string propertyName, string typeName, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, EnumDeclaration enumDeclaration, string documentation, bool nestedContent, bool requiredField)
730
+ {
731
+ string fieldName = String.Concat(propertyName.Substring(0, 1).ToLower(), propertyName.Substring(1), "Field");
732
+ string fieldNameSet = String.Concat(fieldName, "Set");
733
+ Type type = GetClrTypeByXmlName(typeName);
734
+ CodeMemberField fieldMember;
735
+ if (type == null)
736
+ {
737
+ fieldMember = new CodeMemberField(typeName, fieldName);
738
+ }
739
+ else
740
+ {
741
+ fieldMember = new CodeMemberField(type, fieldName);
742
+ }
743
+ fieldMember.Attributes = MemberAttributes.Private;
744
+ typeDeclaration.Members.Add(fieldMember);
745
+ typeDeclaration.Members.Add(new CodeMemberField(typeof(bool), fieldNameSet));
746
+
747
+ CodeMemberProperty propertyMember = new CodeMemberProperty();
748
+ propertyMember.Attributes = MemberAttributes.Public | MemberAttributes.Final;
749
+ if (documentation != null)
750
+ {
751
+ GenerateSummaryComment(propertyMember.Comments, documentation);
752
+ }
753
+ propertyMember.Name = propertyName;
754
+ if (type == null)
755
+ {
756
+ propertyMember.Type = new CodeTypeReference(typeName);
757
+ }
758
+ else
759
+ {
760
+ propertyMember.Type = new CodeTypeReference(type);
761
+ }
762
+
763
+ if (propertyMember.Name.StartsWith("src"))
764
+ {
765
+ propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly"));
766
+ }
767
+ else if (StronglyTypedClasses.multiUppercaseNameRegex.Match(propertyMember.Name).Success)
768
+ {
769
+ propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased"));
770
+ }
771
+
772
+ CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement();
773
+ returnStatement.Expression = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
774
+ propertyMember.GetStatements.Add(returnStatement);
775
+
776
+ CodeAssignStatement assignmentStatement = new CodeAssignStatement();
777
+ propertyMember.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet), new CodePrimitiveExpression(true)));
778
+ assignmentStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
779
+ assignmentStatement.Right = new CodePropertySetValueReferenceExpression();
780
+ propertyMember.SetStatements.Add(assignmentStatement);
781
+
782
+ CodeConditionStatement fieldSetStatement = new CodeConditionStatement();
783
+ fieldSetStatement.Condition = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet);
784
+
785
+ CodeAssignStatement fieldSetAttrStatement = new CodeAssignStatement();
786
+ fieldSetAttrStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet);
787
+ fieldSetAttrStatement.Right = new CodePrimitiveExpression(true);
788
+
789
+ CodeConditionStatement attributeNameMatchStatement = new CodeConditionStatement();
790
+ attributeNameMatchStatement.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), CodeBinaryOperatorType.IdentityEquality, new CodeVariableReferenceExpression("name"));
791
+
792
+ string clrTypeName = (string)simpleTypeNamesToClrTypeNames[typeName];
793
+ switch (clrTypeName)
794
+ {
795
+ case "string":
796
+ if (nestedContent)
797
+ {
798
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
799
+ attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value")));
800
+ }
801
+ else
802
+ {
803
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
804
+ attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value")));
805
+ }
806
+ break;
807
+ case "bool":
808
+ if (nestedContent)
809
+ {
810
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
811
+ }
812
+ else
813
+ {
814
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
815
+ }
816
+ attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Convert"), "ToBoolean", new CodeVariableReferenceExpression("value"), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
817
+ break;
818
+ case "int":
819
+ case "long":
820
+ if (nestedContent)
821
+ {
822
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
823
+ }
824
+ else
825
+ {
826
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
827
+ }
828
+ attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Convert"), "ToInt32", new CodeVariableReferenceExpression("value"), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
829
+ break;
830
+ default:
831
+ if (typeName == "DateTime")
832
+ {
833
+ if (nestedContent)
834
+ {
835
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
836
+ }
837
+ else
838
+ {
839
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePrimitiveExpression("yyyy-MM-ddTHH:mm:ss"), new CodePropertyReferenceExpression(new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"), "DateTimeFormat"))));
840
+ }
841
+ attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Convert"), "ToDateTime", new CodeVariableReferenceExpression("value"), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
842
+ break;
843
+ }
844
+
845
+ if (enumDeclaration == null)
846
+ {
847
+ GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, typeNamesToEnumDeclarations[typeName], fieldName, propertyName);
848
+ }
849
+ else
850
+ {
851
+ GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, enumDeclaration, fieldName, propertyName);
852
+ }
853
+ break;
854
+ }
855
+
856
+ attributeNameMatchStatement.TrueStatements.Add(fieldSetAttrStatement);
857
+
858
+ // TODO: Add throw to falseStatements if required field not set.
859
+ outputXmlMethod.Statements.Add(fieldSetStatement);
860
+ setAttributeMethod.Statements.Add(attributeNameMatchStatement);
861
+
862
+ typeDeclaration.Members.Add(propertyMember);
863
+ }
864
+
865
+ /// <summary>
866
+ /// Generates output for an enum type. Will generate a switch statement for normal enums, and if statements
867
+ /// for a flags enum.
868
+ /// </summary>
869
+ /// <param name="fieldSetStatement">If statement to add statements to.</param>
870
+ /// <param name="attributeNameMatchStatement">If statement to add statements to.</param>
871
+ /// <param name="enumDeclaration">Enum declaration for this field. Could be locally defined enum or global.</param>
872
+ /// <param name="fieldName">Name of the private field.</param>
873
+ /// <param name="propertyName">Name of the property (and XML attribute).</param>
874
+ private static void GenerateOutputForEnum(CodeConditionStatement fieldSetStatement, CodeConditionStatement attributeNameMatchStatement, EnumDeclaration enumDeclaration, string fieldName, string propertyName)
875
+ {
876
+ CodeTypeDeclaration enumParent = enumsToParseMethodClasses[enumDeclaration];
877
+
878
+ if (enumDeclaration.Flags)
879
+ {
880
+ CodeVariableDeclarationStatement outputValueVariable = new CodeVariableDeclarationStatement(typeof(string), "outputValue", new CodeSnippetExpression("\"\""));
881
+ fieldSetStatement.TrueStatements.Add(outputValueVariable);
882
+
883
+ foreach (string key in enumDeclaration.Values)
884
+ {
885
+ CodeConditionStatement enumIfStatement = new CodeConditionStatement();
886
+ enumIfStatement.Condition = new CodeBinaryOperatorExpression(new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), CodeBinaryOperatorType.BitwiseAnd, new CodePropertyReferenceExpression(new CodeSnippetExpression(enumDeclaration.Name), MakeEnumValue(key))), CodeBinaryOperatorType.IdentityInequality, new CodeSnippetExpression("0"));
887
+ CodeConditionStatement lengthIfStatement = new CodeConditionStatement();
888
+ lengthIfStatement.Condition = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("outputValue"), "Length"), CodeBinaryOperatorType.IdentityInequality, new CodeSnippetExpression("0"));
889
+ lengthIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression("\" \""))));
890
+ enumIfStatement.TrueStatements.Add(lengthIfStatement);
891
+ enumIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression(String.Concat("\"", key, "\"")))));
892
+ fieldSetStatement.TrueStatements.Add(enumIfStatement);
893
+ }
894
+
895
+ attributeNameMatchStatement.TrueStatements.Add(new CodeMethodInvokeExpression(
896
+ new CodeTypeReferenceExpression(enumParent.Name),
897
+ String.Concat("TryParse", enumDeclaration.Name),
898
+ new CodeVariableReferenceExpression("value"),
899
+ new CodeDirectionExpression(FieldDirection.Out, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName))));
900
+
901
+ fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("outputValue"))));
902
+ }
903
+ else
904
+ {
905
+ foreach (string key in enumDeclaration.Values)
906
+ {
907
+ CodeConditionStatement enumOutStatement = new CodeConditionStatement();
908
+ enumOutStatement.Condition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), CodeBinaryOperatorType.ValueEquality, new CodePropertyReferenceExpression(new CodeSnippetExpression(enumDeclaration.Name), MakeEnumValue(key)));
909
+ enumOutStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("\"", key, "\""))));
910
+ fieldSetStatement.TrueStatements.Add(enumOutStatement);
911
+ }
912
+
913
+ attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(
914
+ new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName),
915
+ new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(enumParent.Name),
916
+ String.Concat("Parse", enumDeclaration.Name),
917
+ new CodeVariableReferenceExpression("value"))));
918
+ }
919
+ }
920
+
921
+ /// <summary>
922
+ /// Generates a summary comment.
923
+ /// </summary>
924
+ /// <param name="comments">Comments collection to add the comments to.</param>
925
+ /// <param name="content">Content of the comment.</param>
926
+ private static void GenerateSummaryComment(CodeCommentStatementCollection comments, string content)
927
+ {
928
+ using (StringWriter sw = new StringWriter())
929
+ {
930
+ XmlTextWriter writer = null;
931
+
932
+ // create the comment as xml to ensure proper escaping of special xml characters
933
+ try
934
+ {
935
+ writer = new XmlTextWriter(sw);
936
+ writer.Indentation = 0;
937
+
938
+ writer.WriteStartElement("summary");
939
+ writer.WriteString(Environment.NewLine);
940
+
941
+ string nextComment;
942
+ int newlineIndex = content.IndexOf(Environment.NewLine);
943
+ int offset = 0;
944
+ while (newlineIndex != -1)
945
+ {
946
+ nextComment = content.Substring(offset, newlineIndex - offset).Trim();
947
+ writer.WriteString(nextComment);
948
+ writer.WriteString(Environment.NewLine);
949
+ offset = newlineIndex + Environment.NewLine.Length;
950
+ newlineIndex = content.IndexOf(Environment.NewLine, offset);
951
+ }
952
+ nextComment = content.Substring(offset).Trim();
953
+ writer.WriteString(nextComment);
954
+ writer.WriteString(Environment.NewLine);
955
+
956
+ writer.WriteEndElement();
957
+ }
958
+ finally
959
+ {
960
+ if (null != writer)
961
+ {
962
+ writer.Close();
963
+ }
964
+ }
965
+
966
+ // create the comment statements (one per line of xml)
967
+ using (StringReader sr = new StringReader(sw.ToString()))
968
+ {
969
+ string line;
970
+
971
+ while (null != (line = sr.ReadLine()))
972
+ {
973
+ comments.Add(new CodeCommentStatement(line, true));
974
+ }
975
+ }
976
+ }
977
+ }
978
+
979
+ /// <summary>
980
+ /// Gets the CLR type for simple XML type.
981
+ /// </summary>
982
+ /// <param name="typeName">Plain text name of type.</param>
983
+ /// <returns>Type corresponding to parameter.</returns>
984
+ private static Type GetClrTypeByXmlName(string typeName)
985
+ {
986
+ switch (typeName)
987
+ {
988
+ case "bool":
989
+ return typeof(bool);
990
+ case "int":
991
+ return typeof(int);
992
+ case "long":
993
+ return typeof(long);
994
+ case "string":
995
+ return typeof(string);
996
+ default:
997
+ return null;
998
+ }
999
+ }
1000
+
1001
+ /// <summary>
1002
+ /// Processes an XmlSchemaSimpleType into corresponding types.
1003
+ /// </summary>
1004
+ /// <param name="simpleType">XmlSchemaSimpleType to be processed.</param>
1005
+ /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
1006
+ private static void ProcessSimpleType(XmlSchemaSimpleType simpleType, CodeNamespace codeNamespace)
1007
+ {
1008
+ EnumDeclaration enumDeclaration;
1009
+ string simpleTypeName = simpleType.Name;
1010
+ string baseTypeName;
1011
+
1012
+ ProcessSimpleType(simpleTypeName, simpleType, false, out enumDeclaration, out baseTypeName);
1013
+
1014
+ simpleTypeNamesToClrTypeNames.Add(simpleTypeName, baseTypeName);
1015
+
1016
+ if (enumDeclaration != null)
1017
+ {
1018
+ codeNamespace.Types.Add(enumDeclaration.TypeDeclaration);
1019
+ typeNamesToEnumDeclarations.Add(simpleTypeName, enumDeclaration);
1020
+ AddEnumHelperMethods(enumDeclaration, codeNamespace);
1021
+ }
1022
+ }
1023
+
1024
+ /// <summary>
1025
+ /// Processes an XmlSchemaSimpleType into corresponding code.
1026
+ /// </summary>
1027
+ /// <param name="simpleTypeName">Name for the type.</param>
1028
+ /// <param name="simpleType">XmlSchemaSimpleType to be processed.</param>
1029
+ /// <param name="codeNamespace">CodeNamespace to be used when outputting code for global types.</param>
1030
+ /// <param name="parentTypeDeclaration">CodeTypeDeclaration to be used when outputting code for nested types.</param>
1031
+ /// <param name="outputXmlMethod">Member method for the OutputXml method for nested types.</param>
1032
+ /// <param name="setAttributeMethod">Member method for the SetAttribute method for nested types.</param>
1033
+ private static void ProcessSimpleType(string simpleTypeName, XmlSchemaSimpleType simpleType, bool nestedType, out EnumDeclaration enumDeclaration, out string baseTypeName)
1034
+ {
1035
+ enumDeclaration = null;
1036
+ baseTypeName = null;
1037
+
1038
+ // XSD supports simpleTypes derived by union, list, or restriction; restrictions can have any
1039
+ // combination of pattern, enumeration, length, and more; lists can contain any other simpleType.
1040
+ // XsdGen, in contrast, only supports a limited set of values...
1041
+ // Unions are weakly supported by just using the first member type
1042
+ // restrictions must either be all enumeration or a single pattern, a list must be of a
1043
+ // single simpleType which itself is only a restriction of enumeration.
1044
+ if (simpleType.Content is XmlSchemaSimpleTypeUnion)
1045
+ {
1046
+ XmlSchemaSimpleTypeUnion union = simpleType.Content as XmlSchemaSimpleTypeUnion;
1047
+ if (union.MemberTypes.Length > 0)
1048
+ {
1049
+ baseTypeName = union.MemberTypes[0].Name;
1050
+ return;
1051
+ }
1052
+ else
1053
+ {
1054
+ baseTypeName = "string";
1055
+ return;
1056
+ }
1057
+ }
1058
+
1059
+ bool listType = false; // XSD lists become [Flag]enums in C#...
1060
+ XmlSchemaSimpleTypeList simpleTypeList = simpleType.Content as XmlSchemaSimpleTypeList;
1061
+ XmlSchemaSimpleTypeRestriction simpleTypeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction;
1062
+
1063
+ if (simpleTypeList != null)
1064
+ {
1065
+ baseTypeName = simpleTypeList.ItemTypeName.Name;
1066
+
1067
+ if (String.IsNullOrEmpty(baseTypeName))
1068
+ {
1069
+ simpleTypeRestriction = simpleTypeList.ItemType.Content as XmlSchemaSimpleTypeRestriction;
1070
+ if (simpleTypeRestriction == null)
1071
+ {
1072
+ string appName = typeof(XsdGen).Assembly.GetName().Name;
1073
+ throw new NotImplementedException(string.Format("{0} does not support a <list> that does not contain a <simpleType>/<restriction>.", appName));
1074
+ }
1075
+
1076
+ listType = true;
1077
+ }
1078
+ else
1079
+ {
1080
+ // We expect to find an existing enum already declared!
1081
+ EnumDeclaration existingEnumDeclaration = typeNamesToEnumDeclarations[baseTypeName];
1082
+ // TODO: do we need to further alter the Flags setter code because of the helper stuff?
1083
+ // As far as I can tell, this code is never exercised by our existing XSDs!
1084
+ existingEnumDeclaration.SetFlags();
1085
+ }
1086
+ }
1087
+
1088
+ if (simpleTypeRestriction == null)
1089
+ {
1090
+ string appName = typeof(XsdGen).Assembly.GetName().Name;
1091
+ throw new NotImplementedException(string.Format("{0} does not understand this simpleType!", appName));
1092
+ }
1093
+
1094
+ bool foundPattern = false;
1095
+ foreach (XmlSchemaFacet facet in simpleTypeRestriction.Facets)
1096
+ {
1097
+ XmlSchemaEnumerationFacet enumFacet = facet as XmlSchemaEnumerationFacet;
1098
+ XmlSchemaPatternFacet patternFacet = facet as XmlSchemaPatternFacet;
1099
+
1100
+ if (enumFacet != null)
1101
+ {
1102
+ if (foundPattern)
1103
+ {
1104
+ string appName = typeof(XsdGen).Assembly.GetName().Name;
1105
+ throw new NotImplementedException(string.Format("{0} does not support restrictions containing both <pattern> and <enumeration>.", appName));
1106
+ }
1107
+
1108
+ if (enumDeclaration == null)
1109
+ {
1110
+ // For nested types, the simple name comes from the attribute name, with "Type" appended
1111
+ // to prevent name collision with the attribute member itself.
1112
+ if (nestedType)
1113
+ {
1114
+ simpleTypeName = String.Concat(simpleTypeName, "Type");
1115
+ }
1116
+ baseTypeName = simpleTypeName;
1117
+
1118
+ string typeDocumentation = GetDocumentation(simpleType.Annotation);
1119
+ enumDeclaration = new EnumDeclaration(simpleTypeName, typeDocumentation);
1120
+ }
1121
+
1122
+ string documentation = GetDocumentation(enumFacet.Annotation);
1123
+ enumDeclaration.AddValue(enumFacet.Value, documentation);
1124
+ }
1125
+
1126
+ if (patternFacet != null)
1127
+ {
1128
+ if (enumDeclaration != null)
1129
+ {
1130
+ string appName = typeof(XsdGen).Assembly.GetName().Name;
1131
+ throw new NotImplementedException(string.Format("{0} does not support restrictions containing both <pattern> and <enumeration>.", appName));
1132
+ }
1133
+
1134
+ if (foundPattern)
1135
+ {
1136
+ string appName = typeof(XsdGen).Assembly.GetName().Name;
1137
+ throw new NotImplementedException(string.Format("{0} does not support restrictions multiple <pattern> elements.", appName));
1138
+ }
1139
+
1140
+ foundPattern = true;
1141
+ }
1142
+ }
1143
+
1144
+ if (enumDeclaration != null && listType)
1145
+ {
1146
+ enumDeclaration.SetFlags();
1147
+ }
1148
+
1149
+ if (String.IsNullOrEmpty(baseTypeName))
1150
+ {
1151
+ baseTypeName = (string)simpleTypeNamesToClrTypeNames[simpleTypeRestriction.BaseTypeName.Name];
1152
+ }
1153
+ }
1154
+
1155
+ /// <summary>
1156
+ /// Creates an attribute declaration indicating generated code including the tool name and version.
1157
+ /// </summary>
1158
+ /// <returns>GeneratedCodeAttribute declearation.</returns>
1159
+ private static CodeAttributeDeclaration GetGeneratedCodeAttribute()
1160
+ {
1161
+ AssemblyName generatorAssemblyName = typeof(XsdGen).Assembly.GetName();
1162
+ return new CodeAttributeDeclaration("GeneratedCode",
1163
+ new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Name)),
1164
+ new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Version.ToString())));
1165
+ }
1166
+
1167
+ /// <summary>
1168
+ /// Creates a code statement to throw an exception if an argument is null.
1169
+ /// </summary>
1170
+ /// <param name="argumentName">Name of the argument to check.</param>
1171
+ /// <param name="nullOrEmpty">True to check for null-or-empty instead of just null</param>
1172
+ /// <returns>Code condition statement.</returns>
1173
+ private static CodeConditionStatement GetArgumentNullCheckStatement(string argumentName, bool nullOrEmpty)
1174
+ {
1175
+ CodeConditionStatement conditionStatement = new CodeConditionStatement();
1176
+ if (nullOrEmpty)
1177
+ {
1178
+ conditionStatement.Condition = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("String"), "IsNullOrEmpty"), new CodeVariableReferenceExpression(argumentName));
1179
+ }
1180
+ else
1181
+ {
1182
+ conditionStatement.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression(argumentName));
1183
+ }
1184
+
1185
+ conditionStatement.TrueStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression("ArgumentNullException", new CodeSnippetExpression(String.Concat("\"", argumentName, "\"")))));
1186
+ return conditionStatement;
1187
+ }
1188
+
1189
+ /// <summary>
1190
+ /// Creates an attribute declaration to suppress a particular code-analysis message.
1191
+ /// </summary>
1192
+ /// <param name="category">Code analysis category, such as "Microsoft.Design"</param>
1193
+ /// <param name="checkId">Code analysis ID number.</param>
1194
+ /// <returns>SuppressMessageAttribute declaration.</returns>
1195
+ private static CodeAttributeDeclaration GetCodeAnalysisSuppressionAttribute(string category, string checkId)
1196
+ {
1197
+ return new CodeAttributeDeclaration("SuppressMessage",
1198
+ new CodeAttributeArgument(new CodePrimitiveExpression(category)),
1199
+ new CodeAttributeArgument(new CodePrimitiveExpression(checkId)));
1200
+ }
1201
+
1202
+ /// <summary>
1203
+ /// Class representing an enum declaration.
1204
+ /// </summary>
1205
+ internal class EnumDeclaration
1206
+ {
1207
+ private string enumTypeName;
1208
+ private CodeTypeDeclaration declaration;
1209
+ private bool flags;
1210
+ private StringCollection enumValues;
1211
+
1212
+ /// <summary>
1213
+ /// Creates a new enum declaration with the given name.
1214
+ /// </summary>
1215
+ /// <param name="enumTypeName">Name of the type for the enum.</param>
1216
+ /// <param name="documentation">Documentation for the enum type.</param>
1217
+ public EnumDeclaration(string enumTypeName, string documentation)
1218
+ {
1219
+ this.enumTypeName = enumTypeName;
1220
+
1221
+ this.declaration = new CodeTypeDeclaration(enumTypeName);
1222
+ this.declaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
1223
+ this.declaration.Attributes = MemberAttributes.Public;
1224
+ this.declaration.IsEnum = true;
1225
+
1226
+ if (documentation != null)
1227
+ {
1228
+ GenerateSummaryComment(this.declaration.Comments, documentation);
1229
+ }
1230
+
1231
+ this.enumValues = new StringCollection();
1232
+ }
1233
+
1234
+ public CodeTypeDeclaration TypeDeclaration
1235
+ {
1236
+ get { return this.declaration; }
1237
+ }
1238
+
1239
+ /// <summary>
1240
+ /// Gets the enumeration values.
1241
+ /// </summary>
1242
+ /// <value>The enumeration values.</value>
1243
+ public ICollection Values
1244
+ {
1245
+ get { return this.enumValues; }
1246
+ }
1247
+
1248
+ /// <summary>
1249
+ /// Gets the enumeration name.
1250
+ /// </summary>
1251
+ /// <value>The enumeration name.</value>
1252
+ public string Name
1253
+ {
1254
+ get { return this.enumTypeName; }
1255
+ }
1256
+
1257
+ /// <summary>
1258
+ /// Gets the enumeration flags property.
1259
+ /// </summary>
1260
+ /// <value>Whether the enumeration is a [Flags] type.</value>
1261
+ public bool Flags
1262
+ {
1263
+ get { return this.flags; }
1264
+ }
1265
+
1266
+ /// <summary>
1267
+ /// Sets the [Flags] property on the enumeration. Once set, this cannot be undone.
1268
+ /// </summary>
1269
+ public void SetFlags()
1270
+ {
1271
+ if (this.flags)
1272
+ {
1273
+ return;
1274
+ }
1275
+
1276
+ this.flags = true;
1277
+
1278
+ this.declaration.CustomAttributes.Add(new CodeAttributeDeclaration("Flags"));
1279
+ SwitchToNoneValue();
1280
+
1281
+ int enumValue = 0;
1282
+ foreach (CodeMemberField enumField in this.declaration.Members)
1283
+ {
1284
+ enumField.InitExpression = new CodeSnippetExpression(enumValue.ToString());
1285
+ if (enumValue == 0)
1286
+ {
1287
+ enumValue = 1;
1288
+ }
1289
+ else
1290
+ {
1291
+ enumValue *= 2;
1292
+ }
1293
+ }
1294
+ }
1295
+
1296
+ private void InjectIllegalAndNotSetValues()
1297
+ {
1298
+ CodeMemberField memberIllegal = new CodeMemberField(typeof(int), "IllegalValue");
1299
+ CodeMemberField memberNotSet = new CodeMemberField(typeof(int), "NotSet");
1300
+
1301
+ memberIllegal.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(int)), "MaxValue");
1302
+ // Using "-1" for "NotSet" ensure that the next value is zero, which is consistent
1303
+ // with older (3.0) behavior.
1304
+ memberNotSet.InitExpression = new CodePrimitiveExpression(-1);
1305
+
1306
+ this.declaration.Members.Insert(0, memberIllegal);
1307
+ this.declaration.Members.Insert(1, memberNotSet);
1308
+ }
1309
+
1310
+ private void SwitchToNoneValue()
1311
+ {
1312
+ if (this.enumValues.Count > 0)
1313
+ {
1314
+ // Remove the "IllegalValue" and "NotSet" values first.
1315
+ this.declaration.Members.RemoveAt(0);
1316
+ this.declaration.Members.RemoveAt(0);
1317
+
1318
+ CodeMemberField memberNone = new CodeMemberField(typeof(int), "None");
1319
+ memberNone.InitExpression = new CodePrimitiveExpression(0);
1320
+
1321
+ this.declaration.Members.Insert(0, memberNone);
1322
+ }
1323
+ }
1324
+
1325
+ /// <summary>
1326
+ /// Add a value to the enumeration.
1327
+ /// </summary>
1328
+ /// <param name="enumValue">The value to add.</param>
1329
+ public void AddValue(string enumValue, string documentation)
1330
+ {
1331
+ if (this.enumValues.Count == 0)
1332
+ {
1333
+ InjectIllegalAndNotSetValues();
1334
+ }
1335
+
1336
+ this.enumValues.Add(enumValue);
1337
+ CodeMemberField memberField = new CodeMemberField(typeof(int), MakeEnumValue(enumValue));
1338
+ //memberField.Attributes
1339
+ this.declaration.Members.Add(memberField);
1340
+ if (documentation != null)
1341
+ {
1342
+ GenerateSummaryComment(memberField.Comments, documentation);
1343
+ }
1344
+ }
1345
+ }
1346
+
1347
+ private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeNamespace codeNamespace)
1348
+ {
1349
+ if (enumHelperClass == null)
1350
+ {
1351
+ enumHelperClass = new CodeTypeDeclaration("Enums");
1352
+ enumHelperClass.CustomAttributes.Add(GetGeneratedCodeAttribute());
1353
+ // The static and final attributes don't seem to get applied, but we'd prefer if they were.
1354
+ enumHelperClass.Attributes = MemberAttributes.Public | MemberAttributes.Static | MemberAttributes.Final;
1355
+ codeNamespace.Types.Add(enumHelperClass);
1356
+ }
1357
+
1358
+ AddEnumHelperMethods(enumDeclaration, enumHelperClass);
1359
+ }
1360
+
1361
+ private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeTypeDeclaration parentType)
1362
+ {
1363
+ CodeTypeReference stringType = new CodeTypeReference(typeof(string));
1364
+ CodeTypeReference boolType = new CodeTypeReference(typeof(bool));
1365
+ CodeTypeReference enumType = new CodeTypeReference(typeof(Enum));
1366
+ CodeTypeReference newEnumType = new CodeTypeReference(enumDeclaration.Name);
1367
+
1368
+ CodePrimitiveExpression falseValue = new CodePrimitiveExpression(false);
1369
+ CodePrimitiveExpression trueValue = new CodePrimitiveExpression(true);
1370
+ CodeMethodReturnStatement returnFalse = new CodeMethodReturnStatement(falseValue);
1371
+ CodeMethodReturnStatement returnTrue = new CodeMethodReturnStatement(trueValue);
1372
+
1373
+ string parseMethodName = String.Concat("Parse", enumDeclaration.Name);
1374
+ string tryParseMethodName = String.Concat("TryParse", enumDeclaration.Name);
1375
+
1376
+ CodeFieldReferenceExpression defaultEnumValue = null;
1377
+ CodeFieldReferenceExpression illegalEnumValue = null;
1378
+ bool addParse = true;
1379
+ if (enumDeclaration.Flags)
1380
+ {
1381
+ defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "None");
1382
+ illegalEnumValue = defaultEnumValue;
1383
+ // Because there's no "IllegalValue" for [Flags] enums, we can't create the Parse()
1384
+ // method. We can still create the TryParse() method, though!
1385
+ addParse = false;
1386
+ }
1387
+ else
1388
+ {
1389
+ defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "NotSet");
1390
+ illegalEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "IllegalValue");
1391
+ }
1392
+
1393
+ if (addParse)
1394
+ {
1395
+ CodeMemberMethod parseNewEnum = new CodeMemberMethod();
1396
+ GenerateSummaryComment(parseNewEnum.Comments, String.Format("Parses a {0} from a string.", enumDeclaration.Name));
1397
+ parseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static;
1398
+ parseNewEnum.Name = parseMethodName;
1399
+ parseNewEnum.ReturnType = newEnumType;
1400
+ parseNewEnum.Parameters.Add(new CodeParameterDeclarationExpression(stringType, "value"));
1401
+
1402
+ parseNewEnum.Statements.Add(new CodeVariableDeclarationStatement(newEnumType, "parsedValue"));
1403
+
1404
+ // Just delegate to the TryParse version...
1405
+ parseNewEnum.Statements.Add(new CodeMethodInvokeExpression(
1406
+ new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(parentType.Name), tryParseMethodName),
1407
+ new CodeArgumentReferenceExpression("value"),
1408
+ new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression("parsedValue"))));
1409
+
1410
+ parseNewEnum.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("parsedValue")));
1411
+ parentType.Members.Add(parseNewEnum);
1412
+ }
1413
+
1414
+ CodeMemberMethod tryParseNewEnum = new CodeMemberMethod();
1415
+ GenerateSummaryComment(tryParseNewEnum.Comments, String.Format("Tries to parse a {0} from a string.", enumDeclaration.Name));
1416
+ tryParseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static;
1417
+ tryParseNewEnum.Name = tryParseMethodName;
1418
+ tryParseNewEnum.ReturnType = boolType;
1419
+ CodeParameterDeclarationExpression valueDeclaration = new CodeParameterDeclarationExpression(stringType, "value");
1420
+ CodeParameterDeclarationExpression parsedValueDeclaration = new CodeParameterDeclarationExpression(newEnumType, "parsedValue");
1421
+ parsedValueDeclaration.Direction = FieldDirection.Out;
1422
+ tryParseNewEnum.Parameters.Add(valueDeclaration);
1423
+ tryParseNewEnum.Parameters.Add(parsedValueDeclaration);
1424
+
1425
+ CodeArgumentReferenceExpression value = new CodeArgumentReferenceExpression(valueDeclaration.Name);
1426
+ CodeArgumentReferenceExpression parsedValue = new CodeArgumentReferenceExpression(parsedValueDeclaration.Name);
1427
+
1428
+ tryParseNewEnum.Statements.Add(new CodeAssignStatement(parsedValue, defaultEnumValue));
1429
+
1430
+ tryParseNewEnum.Statements.Add(new CodeConditionStatement(
1431
+ new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(stringType), "IsNullOrEmpty"), value),
1432
+ returnFalse));
1433
+
1434
+ // The structure is similar, but distinct, for regular and flag-style enums. In particular,
1435
+ // for a flags-style enum we have to be able to parse multiple values, separated by
1436
+ // spaces, and each value is bitwise-OR'd together.
1437
+ CodeStatementCollection nestedIfParent = tryParseNewEnum.Statements;
1438
+ CodeExpression valueToTest = value;
1439
+
1440
+ // For Flags-style enums, we need to loop over the space-separated values...
1441
+ if (enumDeclaration.Flags)
1442
+ {
1443
+ CodeVariableDeclarationStatement split = new CodeVariableDeclarationStatement(typeof(string[]), "splitValue",
1444
+ new CodeMethodInvokeExpression(value, "Split",
1445
+ new CodeMethodInvokeExpression(new CodePrimitiveExpression(" \t\r\n"), "ToCharArray"),
1446
+ new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(StringSplitOptions)), "RemoveEmptyEntries")));
1447
+ tryParseNewEnum.Statements.Add(split);
1448
+
1449
+ CodeIterationStatement flagLoop = new CodeIterationStatement(
1450
+ new CodeVariableDeclarationStatement(typeof(IEnumerator), "enumerator",
1451
+ new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(split.Name), "GetEnumerator")),
1452
+ new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext"),
1453
+ new CodeSnippetStatement(""));
1454
+ tryParseNewEnum.Statements.Add(flagLoop);
1455
+
1456
+ CodeVariableDeclarationStatement currentValue = new CodeVariableDeclarationStatement(typeof(string), "currentValue",
1457
+ new CodeCastExpression(stringType,
1458
+ new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current")));
1459
+ flagLoop.Statements.Add(currentValue);
1460
+ valueToTest = new CodeVariableReferenceExpression(currentValue.Name);
1461
+
1462
+ nestedIfParent = flagLoop.Statements;
1463
+ }
1464
+
1465
+ // We can't just Enum.Parse, because some values are also keywords (like 'string', 'int', 'default'),
1466
+ // and these get generated as '@'-prefixed values. Instead, we 'switch' on the value and do it manually.
1467
+ // Actually, we if/else, because CodeDom doesn't support 'switch'! Also, we nest the successive 'if's
1468
+ // in order to short-circuit the parsing as soon as there's a match.
1469
+ foreach (string enumValue in enumDeclaration.Values)
1470
+ {
1471
+ CodeFieldReferenceExpression enumValueReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), MakeEnumValue(enumValue));
1472
+ CodeConditionStatement ifStatement = new CodeConditionStatement(
1473
+ new CodeBinaryOperatorExpression(new CodePrimitiveExpression(enumValue), CodeBinaryOperatorType.ValueEquality, valueToTest));
1474
+ if (enumDeclaration.Flags)
1475
+ {
1476
+ ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue,
1477
+ new CodeBinaryOperatorExpression(parsedValue, CodeBinaryOperatorType.BitwiseOr, enumValueReference)));
1478
+ }
1479
+ else
1480
+ {
1481
+ ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue, enumValueReference));
1482
+ }
1483
+ nestedIfParent.Add(ifStatement);
1484
+ nestedIfParent = ifStatement.FalseStatements;
1485
+ }
1486
+
1487
+ // Finally, if we didn't find a match, it's illegal (or none, for flags)!
1488
+ nestedIfParent.Add(new CodeAssignStatement(parsedValue, illegalEnumValue));
1489
+ nestedIfParent.Add(returnFalse);
1490
+
1491
+ tryParseNewEnum.Statements.Add(returnTrue);
1492
+
1493
+ parentType.Members.Add(tryParseNewEnum);
1494
+
1495
+ enumsToParseMethodClasses.Add(enumDeclaration, parentType);
1496
+ }
1497
+ }
1498
+}
src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
new
+22
@@ -0,0 +1,22 @@
1
+<Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <!-- <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> -->
5
+ <TargetFrameworks>net461</TargetFrameworks>
6
+ <IsTool>true</IsTool>
7
+ <IncludeBuildOutput>false</IncludeBuildOutput>
8
+ <OutputType>Exe</OutputType>
9
+ </PropertyGroup>
10
+
11
+ <ItemGroup>
12
+ <Content Include="build\WixBuildTools.XsdGen.targets" PackagePath="build\" />
13
+ <Content Include="buildCrossTargeting\WixBuildTools.XsdGen.targets" PackagePath="buildCrossTargeting\" />
14
+
15
+ <Content Include="$(OutputPath)net461\$(TargetFileName)" PackagePath="tools\full" />
16
+ <!-- <Content Include="$(OutputPath)netcoreapp2.0\$(TargetFileName)" PackagePath="tools\core" /> -->
17
+ </ItemGroup>
18
+
19
+ <ItemGroup>
20
+ <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.37-beta" PrivateAssets="all" />
21
+ </ItemGroup>
22
+</Project>
src/WixBuildTools.XsdGen/XsdGen.cs
new
+124
@@ -0,0 +1,124 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Tools
4
+{
5
+ using System;
6
+ using System.CodeDom;
7
+ using System.CodeDom.Compiler;
8
+ using System.Collections;
9
+ using System.IO;
10
+ using System.Xml;
11
+ using System.Xml.Schema;
12
+ using Microsoft.CSharp;
13
+ using WixToolset;
14
+
15
+ /// <summary>
16
+ /// Generates a strongly-typed C# class from an XML schema (XSD).
17
+ /// </summary>
18
+ public class XsdGen
19
+ {
20
+ private string xsdFile;
21
+ private string outFile;
22
+ private string outputNamespace;
23
+ private string commonNamespace;
24
+ private bool showHelp;
25
+
26
+ /// <summary>
27
+ /// Constructor for the XsdGen class.
28
+ /// </summary>
29
+ /// <param name="args">Command-line arguments passed to the program.</param>
30
+ private XsdGen(string[] args)
31
+ {
32
+ this.ParseCommandlineArgs(args);
33
+
34
+ // show usage information
35
+ if (this.showHelp)
36
+ {
37
+ Console.WriteLine("usage: XsdGen.exe <schema>.xsd <outputFile> <namespace> [<commonNamespace>]");
38
+ return;
39
+ }
40
+
41
+ // ensure that the schema file exists
42
+ if (!File.Exists(this.xsdFile))
43
+ {
44
+ throw new ApplicationException(String.Format("Schema file does not exist: '{0}'.", this.xsdFile));
45
+ }
46
+
47
+ XmlSchema document = null;
48
+ using (StreamReader xsdFileReader = new StreamReader(this.xsdFile))
49
+ {
50
+ document = XmlSchema.Read(xsdFileReader, new ValidationEventHandler(this.ValidationHandler));
51
+ }
52
+
53
+ CodeCompileUnit codeCompileUnit = StronglyTypedClasses.Generate(document, this.outputNamespace, this.commonNamespace);
54
+
55
+ using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
56
+ {
57
+ ICodeGenerator generator = codeProvider.CreateGenerator();
58
+
59
+ CodeGeneratorOptions options = new CodeGeneratorOptions();
60
+ options.BlankLinesBetweenMembers = true;
61
+ options.BracingStyle = "C";
62
+ options.IndentString = " ";
63
+
64
+ using (StreamWriter csharpFileWriter = new StreamWriter(this.outFile))
65
+ {
66
+ generator.GenerateCodeFromCompileUnit(codeCompileUnit, csharpFileWriter, options);
67
+ }
68
+ }
69
+ }
70
+
71
+ /// <summary>
72
+ /// The main entry point for the application.
73
+ /// </summary>
74
+ /// <param name="args">The command line arguments.</param>
75
+ /// <returns>The error code.</returns>
76
+ [STAThread]
77
+ public static int Main(string[] args)
78
+ {
79
+ try
80
+ {
81
+ XsdGen xsdGen = new XsdGen(args);
82
+ }
83
+ catch (Exception e)
84
+ {
85
+ Console.WriteLine("XsdGen.exe : fatal error MSF0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace);
86
+ return 1;
87
+ }
88
+
89
+ return 0;
90
+ }
91
+
92
+ /// <summary>
93
+ /// Validation event handler.
94
+ /// </summary>
95
+ /// <param name="sender">Sender for the event.</param>
96
+ /// <param name="e">Event args.</param>
97
+ public void ValidationHandler(object sender, ValidationEventArgs e)
98
+ {
99
+ }
100
+
101
+ /// <summary>
102
+ /// Parse the command line arguments.
103
+ /// </summary>
104
+ /// <param name="args">Command-line arguments.</param>
105
+ private void ParseCommandlineArgs(string[] args)
106
+ {
107
+ if (3 > args.Length)
108
+ {
109
+ this.showHelp = true;
110
+ }
111
+ else
112
+ {
113
+ this.xsdFile = args[0];
114
+ this.outFile = args[1];
115
+ this.outputNamespace = args[2];
116
+
117
+ if (args.Length >= 4)
118
+ {
119
+ this.commonNamespace = args[3];
120
+ }
121
+ }
122
+ }
123
+ }
124
+}
src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets
new
+67
@@ -0,0 +1,67 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5
+ <PropertyGroup>
6
+ <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
7
+ </PropertyGroup>
8
+
9
+ <ItemGroup>
10
+ <!--Provide support for setting type (BuildAction) from VS-->
11
+ <AvailableItemName Include="XsdGenSource" />
12
+ </ItemGroup>
13
+
14
+ <PropertyGroup>
15
+ <!-- <XsdGenPath Condition=" '$(XsdGenPath)'=='' and '$(MSBuildRuntimeType)'=='Core' ">$(MSBuildThisFileDirectory)..\tools\core\</XsdGenPath> -->
16
+ <XsdGenPath Condition=" '$(XsdGenPath)'=='' ">$(MSBuildThisFileDirectory)..\tools\full\</XsdGenPath>
17
+ </PropertyGroup>
18
+
19
+ <!--
20
+ ================================================================================================
21
+ XsdGen
22
+
23
+ Generates a .cs class file from an .xsd file.
24
+
25
+ [IN]
26
+ @(XsdGenSource) - The items to run through the XsdGen tool.
27
+
28
+ [OUT]
29
+ $(IntermediateOutputPath)%(Filename).cs - The generated .cs files to include in the compilation.
30
+ ================================================================================================
31
+ -->
32
+ <PropertyGroup>
33
+ <XsdGenDependsOn>
34
+ </XsdGenDependsOn>
35
+ <PrepareResourcesDependsOn>
36
+ XsdGen;
37
+ $(PrepareResourcesDependsOn)
38
+ </PrepareResourcesDependsOn>
39
+ </PropertyGroup>
40
+ <Target
41
+ Name="XsdGen"
42
+ BeforeTargets="PrepareResources"
43
+ DependsOnTargets="$(XsdGenDependsOn)"
44
+ Condition=" '@(XsdGenSource)' != '' "
45
+ Inputs="$(MSBuildAllProjects);@(XsdGenSource)"
46
+ Outputs="$(IntermediateOutputPath)%(XsdGenSource.Filename).cs">
47
+
48
+ <PropertyGroup>
49
+ <XsdGenCsFile>$(IntermediateOutputPath)%(XsdGenSource.Filename).cs</XsdGenCsFile>
50
+ <XsdGenCommonNamespace>%(XsdGenSource.CommonNamespace)</XsdGenCommonNamespace>
51
+ </PropertyGroup>
52
+
53
+ <Exec Command=""$(XsdGenPath)WixBuildTools.XsdGen.exe" "%(XsdGenSource.FullPath)" "$(XsdGenCsFile)" %(XsdGenSource.Namespace) $(XsdGenCommonNamespace)"
54
+ Outputs="$(XsdGenCsFile)" />
55
+
56
+ <ItemGroup>
57
+ <!-- This will tell MSBuild to clean up the .cs file during a Clean build -->
58
+ <FileWrites Include="$(XsdGenCsFile)" />
59
+
60
+ <!-- Add the generated .cs file to the list of source files to compile -->
61
+ <Compile Include="$(XsdGenCsFile)">
62
+ <Link>%(XsdGenCsFile.Filename)%(XsdGenCsFile.Extension)</Link>
63
+ </Compile>
64
+ </ItemGroup>
65
+ </Target>
66
+
67
+</Project>
src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets
new
+6
@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5
+ <Import Project="..\build\WixBuildTools.XsdGen.targets" />
6
+</Project>
src/nuget.config
new
+10
@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<configuration>
3
+ <config>
4
+ <add key="repositorypath" value="packages" />
5
+ </config>
6
+ <packageSources>
7
+ <clear />
8
+ <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
9
+ </packageSources>
10
+</configuration>
\ No newline at end of file
src/version.json
new
+11
@@ -0,0 +1,11 @@
1
+{
2
+ "version": "4.0",
3
+ "publicReleaseRefSpec": [
4
+ "^refs/heads/master$"
5
+ ],
6
+ "cloudBuild": {
7
+ "buildNumber": {
8
+ "enabled": true
9
+ }
10
+ }
11
+}