diff options
Diffstat (limited to 'src/WixToolset.Data.WindowsInstaller/ColumnDefinition.cs')
-rw-r--r-- | src/WixToolset.Data.WindowsInstaller/ColumnDefinition.cs | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/src/WixToolset.Data.WindowsInstaller/ColumnDefinition.cs b/src/WixToolset.Data.WindowsInstaller/ColumnDefinition.cs new file mode 100644 index 00000000..7e5a07c5 --- /dev/null +++ b/src/WixToolset.Data.WindowsInstaller/ColumnDefinition.cs | |||
@@ -0,0 +1,1032 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Data | ||
4 | { | ||
5 | using System; | ||
6 | using System.Globalization; | ||
7 | using System.Xml; | ||
8 | |||
9 | /// <summary> | ||
10 | /// Defines MSI column types. | ||
11 | /// </summary> | ||
12 | public enum ColumnType | ||
13 | { | ||
14 | /// <summary>Unknown column type, default and invalid.</summary> | ||
15 | Unknown, | ||
16 | |||
17 | /// <summary>Column is a string.</summary> | ||
18 | String, | ||
19 | |||
20 | /// <summary>Column is a localizable string.</summary> | ||
21 | Localized, | ||
22 | |||
23 | /// <summary>Column is a number.</summary> | ||
24 | Number, | ||
25 | |||
26 | /// <summary>Column is a binary stream.</summary> | ||
27 | Object, | ||
28 | |||
29 | /// <summary>Column is a string that is preserved in transforms (like Object).</summary> | ||
30 | Preserved, | ||
31 | } | ||
32 | |||
33 | /// <summary> | ||
34 | /// Specifies if the column should be modularized. | ||
35 | /// </summary> | ||
36 | public enum ColumnModularizeType | ||
37 | { | ||
38 | /// <summary>Column should not be modularized.</summary> | ||
39 | None, | ||
40 | |||
41 | /// <summary>Column should be modularized.</summary> | ||
42 | Column, | ||
43 | |||
44 | /// <summary>When the column is an primary or foreign key to the Icon table it should be modularized special.</summary> | ||
45 | Icon, | ||
46 | |||
47 | /// <summary>When the column is a companion file it should be modularized.</summary> | ||
48 | CompanionFile, | ||
49 | |||
50 | /// <summary>Column is a condition and should be modularized.</summary> | ||
51 | Condition, | ||
52 | |||
53 | /// <summary>Special modularization type for the ControlEvent table's Argument column.</summary> | ||
54 | ControlEventArgument, | ||
55 | |||
56 | /// <summary>Special modularization type for the Control table's Text column.</summary> | ||
57 | ControlText, | ||
58 | |||
59 | /// <summary>Any Properties in the column should be modularized.</summary> | ||
60 | Property, | ||
61 | |||
62 | /// <summary>Semi-colon list of keys, all of which need to be modularized.</summary> | ||
63 | SemicolonDelimited, | ||
64 | } | ||
65 | |||
66 | /// <summary> | ||
67 | /// Column validation category type | ||
68 | /// </summary> | ||
69 | public enum ColumnCategory | ||
70 | { | ||
71 | /// <summary>Unknown category, default and invalid.</summary> | ||
72 | Unknown, | ||
73 | |||
74 | /// <summary>Text category.</summary> | ||
75 | Text, | ||
76 | |||
77 | /// <summary>UpperCase category.</summary> | ||
78 | UpperCase, | ||
79 | |||
80 | /// <summary>LowerCase category.</summary> | ||
81 | LowerCase, | ||
82 | |||
83 | /// <summary>Integer category.</summary> | ||
84 | Integer, | ||
85 | |||
86 | /// <summary>DoubleInteger category.</summary> | ||
87 | DoubleInteger, | ||
88 | |||
89 | /// <summary>TimeDate category.</summary> | ||
90 | TimeDate, | ||
91 | |||
92 | /// <summary>Identifier category.</summary> | ||
93 | Identifier, | ||
94 | |||
95 | /// <summary>Property category.</summary> | ||
96 | Property, | ||
97 | |||
98 | /// <summary>Filename category.</summary> | ||
99 | Filename, | ||
100 | |||
101 | /// <summary>WildCardFilename category.</summary> | ||
102 | WildCardFilename, | ||
103 | |||
104 | /// <summary>Path category.</summary> | ||
105 | Path, | ||
106 | |||
107 | /// <summary>Paths category.</summary> | ||
108 | Paths, | ||
109 | |||
110 | /// <summary>AnyPath category.</summary> | ||
111 | AnyPath, | ||
112 | |||
113 | /// <summary>DefaultDir category.</summary> | ||
114 | DefaultDir, | ||
115 | |||
116 | /// <summary>RegPath category.</summary> | ||
117 | RegPath, | ||
118 | |||
119 | /// <summary>Formatted category.</summary> | ||
120 | Formatted, | ||
121 | |||
122 | /// <summary>Template category.</summary> | ||
123 | Template, | ||
124 | |||
125 | /// <summary>Condition category.</summary> | ||
126 | Condition, | ||
127 | |||
128 | /// <summary>Guid category.</summary> | ||
129 | Guid, | ||
130 | |||
131 | /// <summary>Version category.</summary> | ||
132 | Version, | ||
133 | |||
134 | /// <summary>Language category.</summary> | ||
135 | Language, | ||
136 | |||
137 | /// <summary>Binary category.</summary> | ||
138 | Binary, | ||
139 | |||
140 | /// <summary>CustomSource category.</summary> | ||
141 | CustomSource, | ||
142 | |||
143 | /// <summary>Cabinet category.</summary> | ||
144 | Cabinet, | ||
145 | |||
146 | /// <summary>Shortcut category.</summary> | ||
147 | Shortcut, | ||
148 | |||
149 | /// <summary>Formatted SDDL category.</summary> | ||
150 | FormattedSDDLText, | ||
151 | } | ||
152 | |||
153 | /// <summary> | ||
154 | /// Definition of a table's column. | ||
155 | /// </summary> | ||
156 | public sealed class ColumnDefinition : IComparable<ColumnDefinition> | ||
157 | { | ||
158 | private string name; | ||
159 | private ColumnType type; | ||
160 | private int length; | ||
161 | private bool primaryKey; | ||
162 | private bool nullable; | ||
163 | private ColumnModularizeType modularize; | ||
164 | private bool localizable; | ||
165 | private bool added; | ||
166 | |||
167 | private bool minValueSet; | ||
168 | private long minValue; | ||
169 | private bool maxValueSet; | ||
170 | private long maxValue; | ||
171 | private string keyTable; | ||
172 | private bool keyColumnSet; | ||
173 | private int keyColumn; | ||
174 | private ColumnCategory category; | ||
175 | private string possibilities; | ||
176 | private string description; | ||
177 | private bool escapeIdtCharacters; | ||
178 | private bool useCData; | ||
179 | |||
180 | /// <summary> | ||
181 | /// Creates a new column definition. | ||
182 | /// </summary> | ||
183 | /// <param name="name">Name of column.</param> | ||
184 | /// <param name="type">Type of column</param> | ||
185 | /// <param name="length">Length of column.</param> | ||
186 | /// <param name="primaryKey">If column is primary key.</param> | ||
187 | /// <param name="nullable">If column is nullable.</param> | ||
188 | /// <param name="modularizeType">Type of modularization for column</param> | ||
189 | /// <param name="localizable">If the column is localizable.</param> | ||
190 | /// <param name="minValueSet">If the minimum of the value was set.</param> | ||
191 | /// <param name="minValue">Minimum value for the column.</param> | ||
192 | /// <param name="maxValueSet">If the maximum value was set.</param> | ||
193 | /// <param name="maxValue">Maximum value for the colum.</param> | ||
194 | /// <param name="keyTable">Optional name of table for foreign key.</param> | ||
195 | /// <param name="keyColumnSet">If the key column was set.</param> | ||
196 | /// <param name="keyColumn">Optional name of column for foreign key.</param> | ||
197 | /// <param name="category">Validation category for column.</param> | ||
198 | /// <param name="possibilities">Set of possible values for column.</param> | ||
199 | /// <param name="description">Description of column in vaidation table.</param> | ||
200 | /// <param name="escapeIdtCharacters">If characters should be escaped in IDT.</param> | ||
201 | /// <param name="useCData">If whitespace should be preserved in a CDATA node.</param> | ||
202 | public ColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool nullable, ColumnModularizeType modularizeType, bool localizable, bool minValueSet, long minValue, bool maxValueSet, long maxValue, string keyTable, bool keyColumnSet, int keyColumn, ColumnCategory category, string possibilities, string description, bool escapeIdtCharacters, bool useCData) | ||
203 | { | ||
204 | this.name = name; | ||
205 | this.type = type; | ||
206 | this.length = length; | ||
207 | this.primaryKey = primaryKey; | ||
208 | this.nullable = nullable; | ||
209 | this.modularize = modularizeType; | ||
210 | this.localizable = localizable; | ||
211 | this.minValueSet = minValueSet; | ||
212 | this.minValue = minValue; | ||
213 | this.maxValueSet = maxValueSet; | ||
214 | this.maxValue = maxValue; | ||
215 | this.keyTable = keyTable; | ||
216 | this.keyColumnSet = keyColumnSet; | ||
217 | this.keyColumn = keyColumn; | ||
218 | this.category = category; | ||
219 | this.possibilities = possibilities; | ||
220 | this.description = description; | ||
221 | this.escapeIdtCharacters = escapeIdtCharacters; | ||
222 | this.useCData = useCData; | ||
223 | } | ||
224 | |||
225 | /// <summary> | ||
226 | /// Gets whether this column was added via a transform. | ||
227 | /// </summary> | ||
228 | /// <value>Whether this column was added via a transform.</value> | ||
229 | public bool Added | ||
230 | { | ||
231 | get { return this.added; } | ||
232 | set { this.added = value; } | ||
233 | } | ||
234 | |||
235 | /// <summary> | ||
236 | /// Gets the name of the column. | ||
237 | /// </summary> | ||
238 | /// <value>Name of column.</value> | ||
239 | public string Name | ||
240 | { | ||
241 | get { return this.name; } | ||
242 | } | ||
243 | |||
244 | /// <summary> | ||
245 | /// Gets the type of the column. | ||
246 | /// </summary> | ||
247 | /// <value>Type of column.</value> | ||
248 | public ColumnType Type | ||
249 | { | ||
250 | get { return this.type; } | ||
251 | } | ||
252 | |||
253 | /// <summary> | ||
254 | /// Gets the length of the column. | ||
255 | /// </summary> | ||
256 | /// <value>Length of column.</value> | ||
257 | public int Length | ||
258 | { | ||
259 | get { return this.length; } | ||
260 | } | ||
261 | |||
262 | /// <summary> | ||
263 | /// Gets if the column is a primary key. | ||
264 | /// </summary> | ||
265 | /// <value>true if column is primary key.</value> | ||
266 | public bool PrimaryKey | ||
267 | { | ||
268 | get { return this.primaryKey; } | ||
269 | } | ||
270 | |||
271 | /// <summary> | ||
272 | /// Gets if the column is nullable. | ||
273 | /// </summary> | ||
274 | /// <value>true if column is nullable.</value> | ||
275 | public bool Nullable | ||
276 | { | ||
277 | get { return this.nullable; } | ||
278 | } | ||
279 | |||
280 | /// <summary> | ||
281 | /// Gets the type of modularization for this column. | ||
282 | /// </summary> | ||
283 | /// <value>Column's modularization type.</value> | ||
284 | public ColumnModularizeType ModularizeType | ||
285 | { | ||
286 | get { return this.modularize; } | ||
287 | } | ||
288 | |||
289 | /// <summary> | ||
290 | /// Gets if the column is localizable. Can be because the type is localizable, or because the column | ||
291 | /// was explicitly set to be so. | ||
292 | /// </summary> | ||
293 | /// <value>true if column is localizable.</value> | ||
294 | public bool IsLocalizable | ||
295 | { | ||
296 | get { return this.localizable || ColumnType.Localized == this.Type; } | ||
297 | } | ||
298 | |||
299 | /// <summary> | ||
300 | /// Gets if the minimum value of the column is set. | ||
301 | /// </summary> | ||
302 | /// <value>true if minimum value is set.</value> | ||
303 | public bool IsMinValueSet | ||
304 | { | ||
305 | get { return this.minValueSet; } | ||
306 | } | ||
307 | |||
308 | /// <summary> | ||
309 | /// Gets the minimum value for the column, only valid if IsMinValueSet returns true. | ||
310 | /// </summary> | ||
311 | /// <value>Minimum value for the column.</value> | ||
312 | public long MinValue | ||
313 | { | ||
314 | get { return this.minValue; } | ||
315 | } | ||
316 | |||
317 | /// <summary> | ||
318 | /// Gets if the maximum value of the column is set. | ||
319 | /// </summary> | ||
320 | /// <value>true if maximum value is set.</value> | ||
321 | public bool IsMaxValueSet | ||
322 | { | ||
323 | get { return this.maxValueSet; } | ||
324 | } | ||
325 | |||
326 | /// <summary> | ||
327 | /// Gets the maximum value for the column, only valid if IsMinValueSet returns true. | ||
328 | /// </summary> | ||
329 | /// <value>Maximum value for the column.</value> | ||
330 | public long MaxValue | ||
331 | { | ||
332 | get { return this.maxValue; } | ||
333 | } | ||
334 | |||
335 | /// <summary> | ||
336 | /// Gets the table that has the foreign key for this column | ||
337 | /// </summary> | ||
338 | /// <value>Foreign key table name.</value> | ||
339 | public string KeyTable | ||
340 | { | ||
341 | get { return this.keyTable; } | ||
342 | } | ||
343 | |||
344 | /// <summary> | ||
345 | /// Gets if the key column is set. | ||
346 | /// </summary> | ||
347 | /// <value>True if the key column is set.</value> | ||
348 | public bool IsKeyColumnSet | ||
349 | { | ||
350 | get { return this.keyColumnSet; } | ||
351 | } | ||
352 | |||
353 | /// <summary> | ||
354 | /// Gets the foreign key column that this column refers to. | ||
355 | /// </summary> | ||
356 | /// <value>Foreign key column.</value> | ||
357 | public int KeyColumn | ||
358 | { | ||
359 | get { return this.keyColumn; } | ||
360 | } | ||
361 | |||
362 | /// <summary> | ||
363 | /// Gets the validation category for this column. | ||
364 | /// </summary> | ||
365 | /// <value>Validation category.</value> | ||
366 | public ColumnCategory Category | ||
367 | { | ||
368 | get { return this.category; } | ||
369 | } | ||
370 | |||
371 | /// <summary> | ||
372 | /// Gets the set of possibilities for this column. | ||
373 | /// </summary> | ||
374 | /// <value>Set of possibilities for this column.</value> | ||
375 | public string Possibilities | ||
376 | { | ||
377 | get { return this.possibilities; } | ||
378 | } | ||
379 | |||
380 | /// <summary> | ||
381 | /// Gets the description for this column. | ||
382 | /// </summary> | ||
383 | /// <value>Description of column.</value> | ||
384 | public string Description | ||
385 | { | ||
386 | get { return this.description; } | ||
387 | } | ||
388 | |||
389 | /// <summary> | ||
390 | /// Gets if characters should be escaped to fit into IDT. | ||
391 | /// </summary> | ||
392 | /// <value>true if data should be escaped when adding to IDT.</value> | ||
393 | public bool EscapeIdtCharacters | ||
394 | { | ||
395 | get { return this.escapeIdtCharacters; } | ||
396 | } | ||
397 | |||
398 | /// <summary> | ||
399 | /// Gets if whitespace should be preserved in a CDATA node. | ||
400 | /// </summary> | ||
401 | /// <value>true if whitespace should be preserved in a CDATA node.</value> | ||
402 | public bool UseCData | ||
403 | { | ||
404 | get { return this.useCData; } | ||
405 | } | ||
406 | |||
407 | /// <summary> | ||
408 | /// Gets the type of the column in IDT format. | ||
409 | /// </summary> | ||
410 | /// <value>IDT format for column type.</value> | ||
411 | public string IdtType | ||
412 | { | ||
413 | get | ||
414 | { | ||
415 | char typeCharacter; | ||
416 | switch (this.type) | ||
417 | { | ||
418 | case ColumnType.Number: | ||
419 | typeCharacter = this.nullable ? 'I' : 'i'; | ||
420 | break; | ||
421 | case ColumnType.Preserved: | ||
422 | case ColumnType.String: | ||
423 | typeCharacter = this.nullable ? 'S' : 's'; | ||
424 | break; | ||
425 | case ColumnType.Localized: | ||
426 | typeCharacter = this.nullable ? 'L' : 'l'; | ||
427 | break; | ||
428 | case ColumnType.Object: | ||
429 | typeCharacter = this.nullable ? 'V' : 'v'; | ||
430 | break; | ||
431 | default: | ||
432 | throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixDataStrings.EXP_UnknownColumnType, this.type)); | ||
433 | } | ||
434 | |||
435 | return String.Concat(typeCharacter, this.length); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | /// <summary> | ||
440 | /// Parses a column definition in a table definition. | ||
441 | /// </summary> | ||
442 | /// <param name="reader">Reader to get data from.</param> | ||
443 | /// <returns>The ColumnDefintion represented by the Xml.</returns> | ||
444 | internal static ColumnDefinition Read(XmlReader reader) | ||
445 | { | ||
446 | if (!reader.LocalName.Equals("columnDefinition")) | ||
447 | { | ||
448 | throw new XmlException(); | ||
449 | } | ||
450 | |||
451 | bool added = false; | ||
452 | ColumnCategory category = ColumnCategory.Unknown; | ||
453 | string description = null; | ||
454 | bool empty = reader.IsEmptyElement; | ||
455 | bool escapeIdtCharacters = false; | ||
456 | int keyColumn = -1; | ||
457 | bool keyColumnSet = false; | ||
458 | string keyTable = null; | ||
459 | int length = -1; | ||
460 | bool localizable = false; | ||
461 | long maxValue = 0; | ||
462 | bool maxValueSet = false; | ||
463 | long minValue = 0; | ||
464 | bool minValueSet = false; | ||
465 | ColumnModularizeType modularize = ColumnModularizeType.None; | ||
466 | string name = null; | ||
467 | bool nullable = false; | ||
468 | string possibilities = null; | ||
469 | bool primaryKey = false; | ||
470 | ColumnType type = ColumnType.Unknown; | ||
471 | bool useCData = false; | ||
472 | |||
473 | // parse the attributes | ||
474 | while (reader.MoveToNextAttribute()) | ||
475 | { | ||
476 | switch (reader.LocalName) | ||
477 | { | ||
478 | case "added": | ||
479 | added = reader.Value.Equals("yes"); | ||
480 | break; | ||
481 | case "category": | ||
482 | switch (reader.Value) | ||
483 | { | ||
484 | case "anyPath": | ||
485 | category = ColumnCategory.AnyPath; | ||
486 | break; | ||
487 | case "binary": | ||
488 | category = ColumnCategory.Binary; | ||
489 | break; | ||
490 | case "cabinet": | ||
491 | category = ColumnCategory.Cabinet; | ||
492 | break; | ||
493 | case "condition": | ||
494 | category = ColumnCategory.Condition; | ||
495 | break; | ||
496 | case "customSource": | ||
497 | category = ColumnCategory.CustomSource; | ||
498 | break; | ||
499 | case "defaultDir": | ||
500 | category = ColumnCategory.DefaultDir; | ||
501 | break; | ||
502 | case "doubleInteger": | ||
503 | category = ColumnCategory.DoubleInteger; | ||
504 | break; | ||
505 | case "filename": | ||
506 | category = ColumnCategory.Filename; | ||
507 | break; | ||
508 | case "formatted": | ||
509 | category = ColumnCategory.Formatted; | ||
510 | break; | ||
511 | case "formattedSddl": | ||
512 | category = ColumnCategory.FormattedSDDLText; | ||
513 | break; | ||
514 | case "guid": | ||
515 | category = ColumnCategory.Guid; | ||
516 | break; | ||
517 | case "identifier": | ||
518 | category = ColumnCategory.Identifier; | ||
519 | break; | ||
520 | case "integer": | ||
521 | category = ColumnCategory.Integer; | ||
522 | break; | ||
523 | case "language": | ||
524 | category = ColumnCategory.Language; | ||
525 | break; | ||
526 | case "lowerCase": | ||
527 | category = ColumnCategory.LowerCase; | ||
528 | break; | ||
529 | case "path": | ||
530 | category = ColumnCategory.Path; | ||
531 | break; | ||
532 | case "paths": | ||
533 | category = ColumnCategory.Paths; | ||
534 | break; | ||
535 | case "property": | ||
536 | category = ColumnCategory.Property; | ||
537 | break; | ||
538 | case "regPath": | ||
539 | category = ColumnCategory.RegPath; | ||
540 | break; | ||
541 | case "shortcut": | ||
542 | category = ColumnCategory.Shortcut; | ||
543 | break; | ||
544 | case "template": | ||
545 | category = ColumnCategory.Template; | ||
546 | break; | ||
547 | case "text": | ||
548 | category = ColumnCategory.Text; | ||
549 | break; | ||
550 | case "timeDate": | ||
551 | category = ColumnCategory.TimeDate; | ||
552 | break; | ||
553 | case "upperCase": | ||
554 | category = ColumnCategory.UpperCase; | ||
555 | break; | ||
556 | case "version": | ||
557 | category = ColumnCategory.Version; | ||
558 | break; | ||
559 | case "wildCardFilename": | ||
560 | category = ColumnCategory.WildCardFilename; | ||
561 | break; | ||
562 | default: | ||
563 | throw new InvalidOperationException(); | ||
564 | } | ||
565 | break; | ||
566 | case "description": | ||
567 | description = reader.Value; | ||
568 | break; | ||
569 | case "escapeIdtCharacters": | ||
570 | escapeIdtCharacters = reader.Value.Equals("yes"); | ||
571 | break; | ||
572 | case "keyColumn": | ||
573 | keyColumnSet = true; | ||
574 | keyColumn = Convert.ToInt32(reader.Value, 10); | ||
575 | break; | ||
576 | case "keyTable": | ||
577 | keyTable = reader.Value; | ||
578 | break; | ||
579 | case "length": | ||
580 | length = Convert.ToInt32(reader.Value, 10); | ||
581 | break; | ||
582 | case "localizable": | ||
583 | localizable = reader.Value.Equals("yes"); | ||
584 | break; | ||
585 | case "maxValue": | ||
586 | maxValueSet = true; | ||
587 | maxValue = Convert.ToInt32(reader.Value, 10); | ||
588 | break; | ||
589 | case "minValue": | ||
590 | minValueSet = true; | ||
591 | minValue = Convert.ToInt32(reader.Value, 10); | ||
592 | break; | ||
593 | case "modularize": | ||
594 | switch (reader.Value) | ||
595 | { | ||
596 | case "column": | ||
597 | modularize = ColumnModularizeType.Column; | ||
598 | break; | ||
599 | case "companionFile": | ||
600 | modularize = ColumnModularizeType.CompanionFile; | ||
601 | break; | ||
602 | case "condition": | ||
603 | modularize = ColumnModularizeType.Condition; | ||
604 | break; | ||
605 | case "controlEventArgument": | ||
606 | modularize = ColumnModularizeType.ControlEventArgument; | ||
607 | break; | ||
608 | case "controlText": | ||
609 | modularize = ColumnModularizeType.ControlText; | ||
610 | break; | ||
611 | case "icon": | ||
612 | modularize = ColumnModularizeType.Icon; | ||
613 | break; | ||
614 | case "none": | ||
615 | modularize = ColumnModularizeType.None; | ||
616 | break; | ||
617 | case "property": | ||
618 | modularize = ColumnModularizeType.Property; | ||
619 | break; | ||
620 | case "semicolonDelimited": | ||
621 | modularize = ColumnModularizeType.SemicolonDelimited; | ||
622 | break; | ||
623 | default: | ||
624 | throw new XmlException(); | ||
625 | } | ||
626 | break; | ||
627 | case "name": | ||
628 | switch (reader.Value) | ||
629 | { | ||
630 | case "CREATE": | ||
631 | case "DELETE": | ||
632 | case "DROP": | ||
633 | case "INSERT": | ||
634 | throw new XmlException(); | ||
635 | default: | ||
636 | name = reader.Value; | ||
637 | break; | ||
638 | } | ||
639 | break; | ||
640 | case "nullable": | ||
641 | nullable = reader.Value.Equals("yes"); | ||
642 | break; | ||
643 | case "primaryKey": | ||
644 | primaryKey = reader.Value.Equals("yes"); | ||
645 | break; | ||
646 | case "set": | ||
647 | possibilities = reader.Value; | ||
648 | break; | ||
649 | case "type": | ||
650 | switch (reader.Value) | ||
651 | { | ||
652 | case "localized": | ||
653 | type = ColumnType.Localized; | ||
654 | break; | ||
655 | case "number": | ||
656 | type = ColumnType.Number; | ||
657 | break; | ||
658 | case "object": | ||
659 | type = ColumnType.Object; | ||
660 | break; | ||
661 | case "string": | ||
662 | type = ColumnType.String; | ||
663 | break; | ||
664 | case "preserved": | ||
665 | type = ColumnType.Preserved; | ||
666 | break; | ||
667 | default: | ||
668 | throw new XmlException(); | ||
669 | } | ||
670 | break; | ||
671 | case "useCData": | ||
672 | useCData = reader.Value.Equals("yes"); | ||
673 | break; | ||
674 | } | ||
675 | } | ||
676 | |||
677 | // parse the child elements (there should be none) | ||
678 | if (!empty) | ||
679 | { | ||
680 | bool done = false; | ||
681 | |||
682 | while (!done && reader.Read()) | ||
683 | { | ||
684 | switch (reader.NodeType) | ||
685 | { | ||
686 | case XmlNodeType.Element: | ||
687 | throw new XmlException(); | ||
688 | case XmlNodeType.EndElement: | ||
689 | done = true; | ||
690 | break; | ||
691 | } | ||
692 | } | ||
693 | |||
694 | if (!done) | ||
695 | { | ||
696 | throw new XmlException(); | ||
697 | } | ||
698 | } | ||
699 | |||
700 | ColumnDefinition columnDefinition = new ColumnDefinition(name, type, length, primaryKey, nullable, modularize, localizable, minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, category, possibilities, description, escapeIdtCharacters, useCData); | ||
701 | columnDefinition.Added = added; | ||
702 | |||
703 | return columnDefinition; | ||
704 | } | ||
705 | |||
706 | /// <summary> | ||
707 | /// Persists a ColumnDefinition in an XML format. | ||
708 | /// </summary> | ||
709 | /// <param name="writer">XmlWriter where the Output should persist itself as XML.</param> | ||
710 | internal void Write(XmlWriter writer) | ||
711 | { | ||
712 | writer.WriteStartElement("columnDefinition", TableDefinitionCollection.XmlNamespaceUri); | ||
713 | |||
714 | writer.WriteAttributeString("name", this.name); | ||
715 | |||
716 | switch (this.type) | ||
717 | { | ||
718 | case ColumnType.Localized: | ||
719 | writer.WriteAttributeString("type", "localized"); | ||
720 | break; | ||
721 | case ColumnType.Number: | ||
722 | writer.WriteAttributeString("type", "number"); | ||
723 | break; | ||
724 | case ColumnType.Object: | ||
725 | writer.WriteAttributeString("type", "object"); | ||
726 | break; | ||
727 | case ColumnType.String: | ||
728 | writer.WriteAttributeString("type", "string"); | ||
729 | break; | ||
730 | case ColumnType.Preserved: | ||
731 | writer.WriteAttributeString("type", "preserved"); | ||
732 | break; | ||
733 | } | ||
734 | |||
735 | writer.WriteAttributeString("length", this.length.ToString(CultureInfo.InvariantCulture.NumberFormat)); | ||
736 | |||
737 | if (this.primaryKey) | ||
738 | { | ||
739 | writer.WriteAttributeString("primaryKey", "yes"); | ||
740 | } | ||
741 | |||
742 | if (this.nullable) | ||
743 | { | ||
744 | writer.WriteAttributeString("nullable", "yes"); | ||
745 | } | ||
746 | |||
747 | if (this.localizable) | ||
748 | { | ||
749 | writer.WriteAttributeString("localizable", "yes"); | ||
750 | } | ||
751 | |||
752 | if (this.added) | ||
753 | { | ||
754 | writer.WriteAttributeString("added", "yes"); | ||
755 | } | ||
756 | |||
757 | switch (this.modularize) | ||
758 | { | ||
759 | case ColumnModularizeType.Column: | ||
760 | writer.WriteAttributeString("modularize", "column"); | ||
761 | break; | ||
762 | case ColumnModularizeType.CompanionFile: | ||
763 | writer.WriteAttributeString("modularize", "companionFile"); | ||
764 | break; | ||
765 | case ColumnModularizeType.Condition: | ||
766 | writer.WriteAttributeString("modularize", "condition"); | ||
767 | break; | ||
768 | case ColumnModularizeType.ControlEventArgument: | ||
769 | writer.WriteAttributeString("modularize", "controlEventArgument"); | ||
770 | break; | ||
771 | case ColumnModularizeType.ControlText: | ||
772 | writer.WriteAttributeString("modularize", "controlText"); | ||
773 | break; | ||
774 | case ColumnModularizeType.Icon: | ||
775 | writer.WriteAttributeString("modularize", "icon"); | ||
776 | break; | ||
777 | case ColumnModularizeType.None: | ||
778 | // this is the default value | ||
779 | break; | ||
780 | case ColumnModularizeType.Property: | ||
781 | writer.WriteAttributeString("modularize", "property"); | ||
782 | break; | ||
783 | case ColumnModularizeType.SemicolonDelimited: | ||
784 | writer.WriteAttributeString("modularize", "semicolonDelimited"); | ||
785 | break; | ||
786 | } | ||
787 | |||
788 | if (this.minValueSet) | ||
789 | { | ||
790 | writer.WriteAttributeString("minValue", this.minValue.ToString(CultureInfo.InvariantCulture.NumberFormat)); | ||
791 | } | ||
792 | |||
793 | if (this.maxValueSet) | ||
794 | { | ||
795 | writer.WriteAttributeString("maxValue", this.maxValue.ToString(CultureInfo.InvariantCulture.NumberFormat)); | ||
796 | } | ||
797 | |||
798 | if (!String.IsNullOrEmpty(this.keyTable)) | ||
799 | { | ||
800 | writer.WriteAttributeString("keyTable", this.keyTable); | ||
801 | } | ||
802 | |||
803 | if (this.keyColumnSet) | ||
804 | { | ||
805 | writer.WriteAttributeString("keyColumn", this.keyColumn.ToString(CultureInfo.InvariantCulture.NumberFormat)); | ||
806 | } | ||
807 | |||
808 | switch (this.category) | ||
809 | { | ||
810 | case ColumnCategory.AnyPath: | ||
811 | writer.WriteAttributeString("category", "anyPath"); | ||
812 | break; | ||
813 | case ColumnCategory.Binary: | ||
814 | writer.WriteAttributeString("category", "binary"); | ||
815 | break; | ||
816 | case ColumnCategory.Cabinet: | ||
817 | writer.WriteAttributeString("category", "cabinet"); | ||
818 | break; | ||
819 | case ColumnCategory.Condition: | ||
820 | writer.WriteAttributeString("category", "condition"); | ||
821 | break; | ||
822 | case ColumnCategory.CustomSource: | ||
823 | writer.WriteAttributeString("category", "customSource"); | ||
824 | break; | ||
825 | case ColumnCategory.DefaultDir: | ||
826 | writer.WriteAttributeString("category", "defaultDir"); | ||
827 | break; | ||
828 | case ColumnCategory.DoubleInteger: | ||
829 | writer.WriteAttributeString("category", "doubleInteger"); | ||
830 | break; | ||
831 | case ColumnCategory.Filename: | ||
832 | writer.WriteAttributeString("category", "filename"); | ||
833 | break; | ||
834 | case ColumnCategory.Formatted: | ||
835 | writer.WriteAttributeString("category", "formatted"); | ||
836 | break; | ||
837 | case ColumnCategory.FormattedSDDLText: | ||
838 | writer.WriteAttributeString("category", "formattedSddl"); | ||
839 | break; | ||
840 | case ColumnCategory.Guid: | ||
841 | writer.WriteAttributeString("category", "guid"); | ||
842 | break; | ||
843 | case ColumnCategory.Identifier: | ||
844 | writer.WriteAttributeString("category", "identifier"); | ||
845 | break; | ||
846 | case ColumnCategory.Integer: | ||
847 | writer.WriteAttributeString("category", "integer"); | ||
848 | break; | ||
849 | case ColumnCategory.Language: | ||
850 | writer.WriteAttributeString("category", "language"); | ||
851 | break; | ||
852 | case ColumnCategory.LowerCase: | ||
853 | writer.WriteAttributeString("category", "lowerCase"); | ||
854 | break; | ||
855 | case ColumnCategory.Path: | ||
856 | writer.WriteAttributeString("category", "path"); | ||
857 | break; | ||
858 | case ColumnCategory.Paths: | ||
859 | writer.WriteAttributeString("category", "paths"); | ||
860 | break; | ||
861 | case ColumnCategory.Property: | ||
862 | writer.WriteAttributeString("category", "property"); | ||
863 | break; | ||
864 | case ColumnCategory.RegPath: | ||
865 | writer.WriteAttributeString("category", "regPath"); | ||
866 | break; | ||
867 | case ColumnCategory.Shortcut: | ||
868 | writer.WriteAttributeString("category", "shortcut"); | ||
869 | break; | ||
870 | case ColumnCategory.Template: | ||
871 | writer.WriteAttributeString("category", "template"); | ||
872 | break; | ||
873 | case ColumnCategory.Text: | ||
874 | writer.WriteAttributeString("category", "text"); | ||
875 | break; | ||
876 | case ColumnCategory.TimeDate: | ||
877 | writer.WriteAttributeString("category", "timeDate"); | ||
878 | break; | ||
879 | case ColumnCategory.UpperCase: | ||
880 | writer.WriteAttributeString("category", "upperCase"); | ||
881 | break; | ||
882 | case ColumnCategory.Version: | ||
883 | writer.WriteAttributeString("category", "version"); | ||
884 | break; | ||
885 | case ColumnCategory.WildCardFilename: | ||
886 | writer.WriteAttributeString("category", "wildCardFilename"); | ||
887 | break; | ||
888 | } | ||
889 | |||
890 | if (!String.IsNullOrEmpty(this.possibilities)) | ||
891 | { | ||
892 | writer.WriteAttributeString("set", this.possibilities); | ||
893 | } | ||
894 | |||
895 | if (!String.IsNullOrEmpty(this.description)) | ||
896 | { | ||
897 | writer.WriteAttributeString("description", this.description); | ||
898 | } | ||
899 | |||
900 | if (this.escapeIdtCharacters) | ||
901 | { | ||
902 | writer.WriteAttributeString("escapeIdtCharacters", "yes"); | ||
903 | } | ||
904 | |||
905 | if (this.useCData) | ||
906 | { | ||
907 | writer.WriteAttributeString("useCData", "yes"); | ||
908 | } | ||
909 | |||
910 | writer.WriteEndElement(); | ||
911 | } | ||
912 | |||
913 | /// <summary> | ||
914 | /// Validate a value for this column. | ||
915 | /// </summary> | ||
916 | /// <param name="value">The value to validate.</param> | ||
917 | /// <returns>Validated value.</returns> | ||
918 | internal object ValidateValue(object value) | ||
919 | { | ||
920 | if (null == value) | ||
921 | { | ||
922 | if (!this.nullable) | ||
923 | { | ||
924 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with a null value because this is a required field.", this.name)); | ||
925 | } | ||
926 | } | ||
927 | else // check numerical values against their specified minimum and maximum values. | ||
928 | { | ||
929 | if (ColumnType.Number == this.type && !this.IsLocalizable) | ||
930 | { | ||
931 | // For now all enums in the tables can be represented by integers. This if statement would need to | ||
932 | // be enhanced if that ever changes. | ||
933 | if (value is int || value.GetType().IsEnum) | ||
934 | { | ||
935 | int intValue = (int)value; | ||
936 | |||
937 | // validate the value against the minimum allowed value | ||
938 | if (this.minValueSet && this.minValue > intValue) | ||
939 | { | ||
940 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is less than the minimum allowed value for this column, {2}.", this.name, intValue, this.minValue)); | ||
941 | } | ||
942 | |||
943 | // validate the value against the maximum allowed value | ||
944 | if (this.maxValueSet && this.maxValue < intValue) | ||
945 | { | ||
946 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is greater than the maximum allowed value for this column, {2}.", this.name, intValue, this.maxValue)); | ||
947 | } | ||
948 | |||
949 | return intValue; | ||
950 | } | ||
951 | else if (value is long) | ||
952 | { | ||
953 | long longValue = (long)value; | ||
954 | |||
955 | // validate the value against the minimum allowed value | ||
956 | if (this.minValueSet && this.minValue > longValue) | ||
957 | { | ||
958 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is less than the minimum allowed value for this column, {2}.", this.name, longValue, this.minValue)); | ||
959 | } | ||
960 | |||
961 | // validate the value against the maximum allowed value | ||
962 | if (this.maxValueSet && this.maxValue < longValue) | ||
963 | { | ||
964 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is greater than the maximum allowed value for this column, {2}.", this.name, longValue, this.maxValue)); | ||
965 | } | ||
966 | |||
967 | return longValue; | ||
968 | } | ||
969 | else | ||
970 | { | ||
971 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set number column '{0}' with a value of type '{1}'.", this.name, value.GetType().ToString())); | ||
972 | } | ||
973 | } | ||
974 | else | ||
975 | { | ||
976 | if (!(value is string)) | ||
977 | { | ||
978 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set string column '{0}' with a value of type '{1}'.", this.name, value.GetType().ToString())); | ||
979 | } | ||
980 | } | ||
981 | } | ||
982 | |||
983 | return value; | ||
984 | } | ||
985 | |||
986 | /// <summary> | ||
987 | /// Compare this column definition to another column definition. | ||
988 | /// </summary> | ||
989 | /// <remarks> | ||
990 | /// Only Windows Installer traits are compared, allowing for updates to WiX-specific table definitions. | ||
991 | /// </remarks> | ||
992 | /// <param name="other">The <see cref="ColumnDefinition"/> to compare with this one.</param> | ||
993 | /// <returns>0 if the columns' core propeties are the same; otherwise, non-0.</returns> | ||
994 | public int CompareTo(ColumnDefinition other) | ||
995 | { | ||
996 | // by definition, this object is greater than null | ||
997 | if (null == other) | ||
998 | { | ||
999 | return 1; | ||
1000 | } | ||
1001 | |||
1002 | // compare column names | ||
1003 | int ret = String.Compare(this.Name, other.Name, StringComparison.Ordinal); | ||
1004 | |||
1005 | // compare column types | ||
1006 | if (0 == ret) | ||
1007 | { | ||
1008 | ret = this.Type == other.Type ? 0 : -1; | ||
1009 | |||
1010 | // compare column lengths | ||
1011 | if (0 == ret) | ||
1012 | { | ||
1013 | ret = this.Length == other.Length ? 0 : -1; | ||
1014 | |||
1015 | // compare whether both are primary keys | ||
1016 | if (0 == ret) | ||
1017 | { | ||
1018 | ret = this.PrimaryKey == other.PrimaryKey ? 0 : -1; | ||
1019 | |||
1020 | // compare nullability | ||
1021 | if (0 == ret) | ||
1022 | { | ||
1023 | ret = this.Nullable == other.Nullable ? 0 : -1; | ||
1024 | } | ||
1025 | } | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | return ret; | ||
1030 | } | ||
1031 | } | ||
1032 | } | ||