aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-08-15 00:36:12 -0700
committerRob Mensching <rob@firegiant.com>2017-08-15 01:31:18 -0700
commit0358780c77469974dd8bc87840364e790ccecc29 (patch)
tree174eda58367c7873f57d64047cb44ba1930c8c73 /src
parent8bba595424c3b1928fc7758e084127ce9064f745 (diff)
downloadwix-0358780c77469974dd8bc87840364e790ccecc29.tar.gz
wix-0358780c77469974dd8bc87840364e790ccecc29.tar.bz2
wix-0358780c77469974dd8bc87840364e790ccecc29.zip
Migrate XsdGen
Diffstat (limited to 'src')
-rw-r--r--src/Directory.Build.props12
-rw-r--r--src/WixBuildTools.XsdGen/AssemblyInfo.cs9
-rw-r--r--src/WixBuildTools.XsdGen/CodeDomInterfaces.cs96
-rw-r--r--src/WixBuildTools.XsdGen/CodeDomReader.cs159
-rw-r--r--src/WixBuildTools.XsdGen/ElementCollection.cs642
-rw-r--r--src/WixBuildTools.XsdGen/StronglyTypedClasses.cs1498
-rw-r--r--src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj22
-rw-r--r--src/WixBuildTools.XsdGen/XsdGen.cs124
-rw-r--r--src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets67
-rw-r--r--src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets6
-rw-r--r--src/nuget.config10
-rw-r--r--src/version.json11
12 files changed, 2656 insertions, 0 deletions
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
new file mode 100644
index 00000000..0f9c550d
--- /dev/null
+++ b/src/Directory.Build.props
@@ -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>
diff --git a/src/WixBuildTools.XsdGen/AssemblyInfo.cs b/src/WixBuildTools.XsdGen/AssemblyInfo.cs
new file mode 100644
index 00000000..b3740b2a
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/AssemblyInfo.cs
@@ -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
3using System;
4using System.Reflection;
5using System.Runtime.InteropServices;
6
7[assembly: AssemblyCulture("")]
8[assembly: CLSCompliant(true)]
9[assembly: ComVisible(false)]
diff --git a/src/WixBuildTools.XsdGen/CodeDomInterfaces.cs b/src/WixBuildTools.XsdGen/CodeDomInterfaces.cs
new file mode 100644
index 00000000..850839d4
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/CodeDomInterfaces.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.XsdGen/CodeDomReader.cs b/src/WixBuildTools.XsdGen/CodeDomReader.cs
new file mode 100644
index 00000000..5198f264
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/CodeDomReader.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.XsdGen/ElementCollection.cs b/src/WixBuildTools.XsdGen/ElementCollection.cs
new file mode 100644
index 00000000..e364dd11
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/ElementCollection.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.XsdGen/StronglyTypedClasses.cs b/src/WixBuildTools.XsdGen/StronglyTypedClasses.cs
new file mode 100644
index 00000000..4a41f8a9
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/StronglyTypedClasses.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj b/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
new file mode 100644
index 00000000..430e290d
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
@@ -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>
diff --git a/src/WixBuildTools.XsdGen/XsdGen.cs b/src/WixBuildTools.XsdGen/XsdGen.cs
new file mode 100644
index 00000000..a1374df3
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/XsdGen.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets b/src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets
new file mode 100644
index 00000000..ca1b89f6
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets
@@ -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>
diff --git a/src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets b/src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets
new file mode 100644
index 00000000..58692095
--- /dev/null
+++ b/src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets
@@ -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>
diff --git a/src/nuget.config b/src/nuget.config
new file mode 100644
index 00000000..4e08a1a9
--- /dev/null
+++ b/src/nuget.config
@@ -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
diff --git a/src/version.json b/src/version.json
new file mode 100644
index 00000000..5f857771
--- /dev/null
+++ b/src/version.json
@@ -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}