@joebigelow / wix-1 / commits / 0358780c

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="&quot;$(XsdGenPath)WixBuildTools.XsdGen.exe&quot; &quot;%(XsdGenSource.FullPath)&quot; &quot;$(XsdGenCsFile)&quot; %(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 +}